----------------------------------------------------------------
-- Global Variables
----------------------------------------------------------------

CharacterWindow = {}

CharacterWindow.timeoutList = {}

-- Constant Strings
CharacterWindow.TOOLTIP_TROPHY_NEXT_LOC = GetString( StringTables.Default.TOOLTIP_TROPHY_NEXT_LOC )
CharacterWindow.TOOLTIP_TROPHY_PREV_LOC = GetString( StringTables.Default.TOOLTIP_TROPHY_PREV_LOC )
CharacterWindow.TOOLTIP_TROPHY_NO_AVAILABLE_LOC = GetString( StringTables.Default.TOOLTIP_TROPHY_NO_AVAILABLE_LOC )
CharacterWindow.TOOLTIP_TROPHY_INVALID_LOC = GetString( StringTables.Default.TOOLTIP_TROPHY_INVALID_LOC )

-- trophy Constants
CharacterWindow.NUM_TROPHY_SLOTS = GameData.Player.c_NUM_TROPHIES
CharacterWindow.NORMAL_TINT = {R=255, G=255, B=255}
CharacterWindow.EQUIPMENT_EMPTY_TINT = {R=204, G=168, B=144}
CharacterWindow.TROPHY_EMPTY_TINT = {R=170, G=140, B=120}
CharacterWindow.TROPHY_INVALID_LOC_TINT = Tooltips.COLOR_WARNING 
CharacterWindow.INVALID_TROPHY_POSITION = 0

-- prevent some invalid initialization
CharacterWindow.initializationComplete = false

CharacterWindow.equipmentData = {}
CharacterWindow.trophyData = {}
CharacterWindow.trophyLocData = {} -- locations where trophies can be attached to
CharacterWindow.temporaryEnhancedItemsSlots = {}
CharacterWindow.marketItemData = nil
CharacterWindow.marketItemDataSlot = -1

CharacterWindow.timeoutsList = {}
CharacterWindow.firstTimeoutsync = true -- this is used to make sure that there has been a sync when the timeout tab is shown
CharacterWindow.timeoutElapsedTime = 0  -- this is the elapsed time, when it gets > 60 I decrement the timeouts and subtract 60

-- trophy variables
CharacterWindow.currentlySelectedTrophyPosition = CharacterWindow.INVALID_TROPHY_POSITION
CharacterWindow.trophyDragStarted = false


-- EquipmentSlotInfo provides default icons and strings when for when no armor is equipped for that slot
CharacterWindow.NUM_EQUIPMENT_SLOTS = 20

CharacterWindow.EquipmentSlotInfo = {  }     
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.RIGHT_HAND]   =  { name=GetString( StringTables.Default.LABEL_RIGHT_HAND ),   iconNum=6}
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.LEFT_HAND]    =  { name=GetString( StringTables.Default.LABEL_LEFT_HAND ),    iconNum=7 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.RANGED]       =  { name=GetString( StringTables.Default.LABEL_RANGED_SLOT ),  iconNum=8 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.BODY]         =  { name=GetString( StringTables.Default.LABEL_BODY ),         iconNum=9}
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.GLOVES]       =  { name=GetString( StringTables.Default.LABEL_GLOVES ),       iconNum=10 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.BOOTS]        =  { name=GetString( StringTables.Default.LABEL_BOOTS ),        iconNum=11 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.HELM]         =  { name=GetString( StringTables.Default.LABEL_HELM ),         iconNum=12 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.SHOULDERS]    =  { name=GetString( StringTables.Default.LABEL_SHOULDERS ),    iconNum=13 }
-- No longer used, the pockets took their place
--CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.SHIRT]        =  { name=GetString( StringTables.Default.LABEL_SHIRT ),        iconNum=14 }
--CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.PANTS]        =  { name=GetString( StringTables.Default.LABEL_PANTS ),        iconNum=15 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.POCKET1]		=  { name=GetString( StringTables.Default.LABEL_POCKET ),   iconNum=36 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.POCKET2]		=  { name=GetString( StringTables.Default.LABEL_POCKET ),   iconNum=36 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.BACK]         =  { name=GetString( StringTables.Default.LABEL_BACK ),         iconNum=16 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.BELT]         =  { name=GetString( StringTables.Default.LABEL_BELT ),         iconNum=17 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.BANNER]       =  { name=GetString( StringTables.Default.LABEL_BANNER ),       iconNum=18 }
-- No longer used, mount may or may not take this place
--CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.TABARD]       =  { name=GetString( StringTables.Default.LABEL_TABARD ),       iconNum=19 }
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.ACCESSORY1]   =  { name=GetString( StringTables.Default.LABEL_ACCESSORY1 ),   iconNum=20 } 
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.ACCESSORY2]   =  { name=GetString( StringTables.Default.LABEL_ACCESSORY2 ),   iconNum=20 } 
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.ACCESSORY3]   =  { name=GetString( StringTables.Default.LABEL_ACCESSORY3 ),   iconNum=20 } 
CharacterWindow.EquipmentSlotInfo[GameData.EquipSlots.ACCESSORY4]   =  { name=GetString( StringTables.Default.LABEL_ACCESSORY4 ),   iconNum=20 }
-- EquipmentSlotInfo for trophies is set programatically in  CharacterWindow.UnlockTrophies()

-- IDs for select stat icons
CharacterWindow.LABEL_MELEE_BONUS = "CharacterWindow.LABEL_MELEE_BONUS"
CharacterWindow.LABEL_MELEE_SPEED = "CharacterWindow.LABEL_MELEE_SPEED"
CharacterWindow.LABEL_MELEE_CRIT_BONUS = "CharacterWindow.LABEL_MELEE_CRIT_BONUS"
CharacterWindow.LABEL_MELEE_CRIT_DAMAGE = "CharacterWindow.LABEL_MELEE_CRIT_DAMAGE"
-- Range selections
CharacterWindow.LABEL_RANGED_SPEED = "CharacterWindow.LABEL_RANGED_SPEED"
CharacterWindow.LABEL_RANGED_BONUS = "CharacterWindow.LABEL_RANGED_BONUS"
CharacterWindow.LABEL_RANGED_CRIT_BONUS = "CharacterWindow.LABEL_RANGED_CRIT_BONUS"
CharacterWindow.LABEL_RANGED_CRIT_DAMAGE = "CharacterWindow.LABEL_RANGED_CRIT_DAMAGE"
-- Spell selections
CharacterWindow.LABEL_SPELL_CRIT_DAMAGE = "CharacterWindow.LABEL_SPELL_CRIT_DAMAGE"

-- Icon info for the stats icons
CharacterWindow.StatIconInfo = {  }
-- Stats selections
CharacterWindow.StatIconInfo[GameData.Stats.STRENGTH]   =  { iconNum=100 }
CharacterWindow.StatIconInfo[GameData.Stats.TOUGHNESS]   =  { iconNum=103 }
CharacterWindow.StatIconInfo[GameData.Stats.WOUNDS]   =  { iconNum=104 }
CharacterWindow.StatIconInfo[GameData.Stats.INITIATIVE]   =  { iconNum=105 }
CharacterWindow.StatIconInfo[GameData.Stats.WEAPONSKILL]   =  { iconNum=106 }
CharacterWindow.StatIconInfo[GameData.Stats.BALLISTICSKILL]   =  { iconNum=107 }
CharacterWindow.StatIconInfo[GameData.Stats.INTELLIGENCE]   =  { iconNum=108 }
CharacterWindow.StatIconInfo[GameData.Stats.WILLPOWER]   =  { iconNum=102 }
-- Defense selections
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_ARMOR]   =  { iconNum=121 }
CharacterWindow.StatIconInfo[GameData.Stats.CORPOREALRESIST]   =  { iconNum=164 }
CharacterWindow.StatIconInfo[GameData.Stats.SPIRITRESIST]   =  { iconNum=155 }
CharacterWindow.StatIconInfo[GameData.Stats.ELEMENTALRESIST]   =  { iconNum=162 }
CharacterWindow.StatIconInfo[GameData.Stats.BLOCKSKILL]   =  { iconNum=165 }
CharacterWindow.StatIconInfo[GameData.Stats.PARRYSKILL]   =  { iconNum=110 }
CharacterWindow.StatIconInfo[GameData.Stats.EVADESKILL]   =  { iconNum=111 }
CharacterWindow.StatIconInfo[GameData.Stats.DISRUPTSKILL]   =  { iconNum=112 }
-- Melee selections
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_WEAPON_DPS]   =  { iconNum=159 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_BONUS]   =  { iconNum=156 }
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_ARMOR_PENETRATION]   =  { iconNum=166 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_SPEED]   =  { iconNum=111 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_CRIT_BONUS]   =  { iconNum=163 }
--CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_CRIT_DAMAGE]   =  { iconNum=163 }
-- Range selections
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_RANGED]   =  { iconNum=157 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_SPEED]   =  { iconNum=111 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_BONUS]   =  { iconNum=156 }
CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_CRIT_BONUS]   =  { iconNum=163 }
--CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_CRIT_DAMAGE]   =  { iconNum=163 }
-- Spell selections
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_SPELL_BONUS]   =  { iconNum=156 }
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_DAMAGE_CRIT_PERCENT]   =  { iconNum=163 }
CharacterWindow.StatIconInfo[StringTables.Default.LABEL_HEAL_CRIT_PERCENT]   =  { iconNum=160 }
--CharacterWindow.StatIconInfo[CharacterWindow.LABEL_SPELL_CRIT_DAMAGE]   =  { iconNum=163 }

-- stat data
CharacterWindow.STAT_ICON_BASE_ID = 100

CharacterWindow.TOOLTIP_ANCHOR = { Point = "topright",  RelativeTo = "CharacterWindow", RelativePoint = "topleft",  XOffset=5, YOffset=165 }

CharacterWindow.FIRST_AVAILABLE_INVENTORY_SLOT = 561

CharacterWindow.currentStatSelection = 1
CharacterWindow.previousStatSelection = 0
CharacterWindow.currentTitleSelection = 0

-- load commonly used trophy icons
--
--      <Icon id="00037" texture="Icons/Ge_Trophy.dds" name="                                                           "/>
--      <Icon id="00038" texture="Icons/ovl_noValidSlot.dds" name="                                                         "/>
--      <Icon id="00039" texture="Icons/ovl_Lock.dds" name="                                                        "/>
--      <Icon id="00040" texture="Icons/ovl_noArmor.dds" name="                                                         "/>

local iconTexture, iconX, iconY = GetIconData( 37 )
CharacterWindow.TROPHY_EMPTY_ICON = {texture=iconTexture, x=iconX, y=iconY }

iconTexture, iconX, iconY = GetIconData( 38 )
CharacterWindow.TROPHY_INVALID_ATTACHMENT_POINT_ICON = {texture=iconTexture, x=iconX, y=iconY }

iconTexture, iconX, iconY = GetIconData( 39 )
CharacterWindow.TROPHY_LOCKED_ICON = {texture=iconTexture, x=iconX, y=iconY }

iconTexture, iconX, iconY = GetIconData( 40 )
CharacterWindow.TROPHY_NO_ATTACHMENT_POINT_ICON = {texture=iconTexture, x=iconX, y=iconY }

CharacterWindow.MODE_NORMAL = 1
CharacterWindow.MODE_DYE_MERCHANT = 2
CharacterWindow.MODE_BRAGS = 3
CharacterWindow.MODE_TIMEOUTS = 4


----------------------------------------------------------------
-- Local Variables
----------------------------------------------------------------

CharacterWindow.dropPending = false
CharacterWindow.MAX_BONUSES_PER_ITEM = 10
CharacterWindow.braggingRights = nil

local g_currentStrength
local g_currentWillpower
local g_currentToughness
local g_currentWounds
local g_currentInitiative
local g_currentWeaponskill
local g_currentBallisticskill
local g_currentIntelligence
local g_currentBlock
local g_currentParry
local g_currentEvade
local g_currentDisrupt
local g_currentSpirit
local g_currentElemental
local g_currentCorporeal


-- Standard Clamp function that could be put in a math utils mod
local function Clamp( value, min, max )
	if( value > max )
	then
		return max
	elseif( value < min )
	then
		return min
	end
	return value
end

----------------------------------------------------------------
-- CharacterWindow Functions
----------------------------------------------------------------

-- OnInitialize Handler
function CharacterWindow.Initialize()

    LabelSetText( "CharacterWindowContentsImageNameLabel", GameData.Player.name )
    LabelSetText( "CharacterWindowBragsHeader", GetStringFormat( StringTables.Default.LABEL_PLAYERS_BRAGS_HEADER, {GameData.Player.name} ) )
    LabelSetText( "CharacterWindowBragsFooter", GetString( StringTables.Default.LABEL_BRAGS_HELP_TEXT ) )

--    CharacterWindow.UpdateTitleLabel()
    CharacterWindow.UpdateCareerRank()

    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_EQUIPMENT_SLOT_UPDATED, "CharacterWindow.UpdateEquipmentSlot")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_TROPHY_SLOT_UPDATED, "CharacterWindow.UpdateTrophySlot")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_STATS_UPDATED, "CharacterWindow.UpdateStatsNew")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_MAX_HIT_POINTS_UPDATED, "CharacterWindow.UpdateHitPoints")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_CAREER_RANK_UPDATED, "CharacterWindow.UpdateCareerRank") 
--  WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_ACTIVE_TITLE_UPDATED, "CharacterWindow.UpdateTitleLabel")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.INTERACT_DYE_MERCHANT, "CharacterWindow.ShowDyeMerchant")  
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.INTERACT_DONE, "CharacterWindow.HideDyeMerchant")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.VISIBLE_EQUIPMENT_UPDATED, "CharacterWindow.OnVisibleEquipmentUpdated")
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_MONEY_UPDATED, "CharacterWindow.UpdateMoney" )
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.SOCIAL_BRAGGING_RIGHTS_UPDATED, "CharacterWindow.BraggingRightsUpdated") 
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.PLAYER_EFFECTS_UPDATED, "CharacterWindow.PlayerEffectsUpdated") 
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.LOCKOUTS_UPDATED, "CharacterWindow.LockoutsUpdated")    

    CharacterWindow.UpdateStatsNew()
    CharacterWindow.UpdateHitPoints()
    CharacterWindow.UpdateCareerRank()

    CharacterWindow.HideTrophyLocArrows()

    CharacterWindow.UpdateStatCombobox()    

    CharacterWindow.UnlockTrophies() 


    -- Update the visible equipment check boxes.
    ButtonSetCheckButtonFlag("CharacterWindowContentsEquipmentShowHelm", true )
    WindowSetShowing("CharacterWindowContentsEquipmentShowHelm", false)
    ButtonSetCheckButtonFlag("CharacterWindowContentsEquipmentShowCloak", true )
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloak", false)
    ButtonSetCheckButtonFlag("CharacterWindowContentsEquipmentShowCloakHeraldry", true )
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloakHeraldry", false)

    local showHelm = GetEquippedItemVisible( GameData.EquipSlots.HELM )
    local showCloak = GetEquippedItemVisible( GameData.EquipSlots.BACK )
    local showCloakHeraldry = IsShowingCloakHeraldry()
    CharacterWindow.OnVisibleEquipmentUpdated( showHelm, showCloak, showCloakHeraldry )

    -- Set up tabs
    ButtonSetText( "CharacterWindowTabsCharTab", GetString( StringTables.Default.LABEL_CHARACTER ) )
    ButtonSetText( "CharacterWindowTabsBragsTab", GetString( StringTables.Default.LABEL_BRAGS ) )
    ButtonSetText( "CharacterWindowTabsTimeoutTab", GetString( StringTables.Default.LABEL_TIMEOUTS ) )

    -- set the timeout tabs title label
    LabelSetText("CharacterWindowTimeoutsTitleLabel", GetString( StringTables.Default.LABEL_TIMEOUT_TITLE ))

    -- Set bragging rights
    CharacterWindow.BraggingRightsUpdated()

    -- reset the background to all black
    CharacterWindow.ResetListBackground()

    CharacterWindow.UpdateMode( CharacterWindow.MODE_NORMAL )
end


-- BRAGGING RIGHTS FUNCTIONS
function CharacterWindow.BraggingRightsUpdated()
    CharacterWindow.braggingRights = GetBraggingRights()
    
    for index, brag in ipairs( CharacterWindow.braggingRights )
    do
        local windowName = "CharacterWindowBragsEntry"..index
        local anchorWindow = "CharacterWindowBragsEntry"..index-1
        if( index <= 1 )
        then
            anchorWindow = "CharacterWindowBragsAnchor"
        end
    
        -- create a window for this brag if it doesn't exist
        if( not DoesWindowExist( windowName ) )
        then
            CreateWindowFromTemplate( windowName, "BraggingRightTemplate", "CharacterWindowBrags" )
            WindowAddAnchor( windowName, "bottom", anchorWindow, "top", 0, 0 )
        end
        
        WindowSetId( windowName, index )
        
        LabelSetText( windowName.."Text", brag.name )

        -- set the reward data
        if( not brag.rewards )
        then
            WindowSetShowing( windowName.."Reward1", false )
            WindowSetShowing( windowName.."Reward2", false )
        else
            TomeWindow.SetTomeReward( windowName.."Reward1", brag.rewards[1] )
            TomeWindow.SetTomeReward( windowName.."Reward2", brag.rewards[2] )
            
            -- Anchor card to left most reward
            anchorCardTo = windowName.."Reward1"
            if( brag.rewards[2] and brag.rewards[2].rewardId ~= 0 )
            then
                anchorCardTo = windowName.."Reward2"
            end
            WindowClearAnchors( windowName.."Card" )
            WindowAddAnchor( windowName.."Card", "topleft", anchorCardTo, "topright", 0, 0 )
        end
        
        -- Set the card if there is one
        local cardData = nil
        if( brag.cardId and brag.cardId ~= 0 )
        then
            cardData = TomeGetCardData( brag.cardId )
        end
        TomeWindow.SetCard( windowName.."Card", cardData )
    end
    
end

function CharacterWindow.RemoveBrag()
    local bragIndex = WindowGetId( WindowGetParent( SystemData.ActiveWindow.name ) )

    SetBraggingRight( bragIndex, 0 )
end

function CharacterWindow.OnClickReward()
    local bragIndex = WindowGetId( WindowGetParent( SystemData.ActiveWindow.name ) )
    local rewardIndex = WindowGetId(  SystemData.ActiveWindow.name )

    local rewardData = CharacterWindow.braggingRights[bragIndex].rewards[rewardIndex]
    TomeWindow.OnClickTomeReward( rewardData )
end

function CharacterWindow.OnMouseOverReward()
    local bragIndex = WindowGetId( WindowGetParent( SystemData.ActiveWindow.name ) )
    local rewardIndex = WindowGetId(  SystemData.ActiveWindow.name )

    local rewardData = CharacterWindow.braggingRights[bragIndex].rewards[rewardIndex]
    TomeWindow.OnMouseOverTomeReward( SystemData.ActiveWindow.name, rewardData )
end

function CharacterWindow.OnClickCard()
    local cardId = WindowGetId( SystemData.ActiveWindow.name )
    
    local cardData = TomeGetCardData( cardId )
    TomeWindow.OnClickTomeCard( cardData )
end

function CharacterWindow.OnMouseOverCard()
    local cardId = WindowGetId( SystemData.ActiveWindow.name )
    
    local cardData = TomeGetCardData( cardId )
    TomeWindow.OnMouseOverTomeCard( SystemData.ActiveWindow.name, cardData, StringTables.Default.TEXT_CLICK_CARD_LINK, Tooltips.ANCHOR_WINDOW_RIGHT )
end

function CharacterWindow.OnTabSelectChar()
    CharacterWindow.UpdateMode( CharacterWindow.MODE_NORMAL )
end

function CharacterWindow.OnTabSelectBrags()
    CharacterWindow.UpdateMode( CharacterWindow.MODE_BRAGS )
end

function CharacterWindow.OnTabSelectTimeouts()
    CharacterWindow.UpdateMode( CharacterWindow.MODE_TIMEOUTS )
end

-- DYE MERCHANT FUNCTIONS
local function GetIndexIntoDyeColors( id )
    return id + 1
end

local function IsSlotValidForDye( slotNum )
    local function MakeOkayDialog( displayString )
        DialogManager.MakeOneButtonDialog( displayString, GetString( StringTables.Default.LABEL_OKAY ), nil )
    end
    
    if( slotNum >= GameData.Player.c_TROPHY_START_INDEX and
        slotNum < CharacterWindow.NUM_TROPHY_SLOTS + GameData.Player.c_TROPHY_START_INDEX )
    then
        MakeOkayDialog( GetString( StringTables.Default.TEXT_CANNOT_DYE_ITEM ) )
        return false
    end
    
    if( slotNum )
    then
        if( CharacterWindow.equipmentData[ slotNum ].id ~= 0 and
            CharacterWindow.equipmentData[ slotNum ].flags[GameData.Item.EITEMFLAG_DYE_ABLE] and
            GetDyeTintMasks( CharacterWindow.equipmentData[ slotNum ].id ) ~=  GameData.TintMasks.NONE )
        then
            if( GetDyeTintMasks( CharacterWindow.equipmentData[ slotNum ].id ) == GameData.TintMasks.A )
            then
                MakeOkayDialog( GetString( StringTables.Default.TEXT_DYE_MERCHANT_PRIMARY_TINT_ONLY ) )
            elseif( GetDyeTintMasks( CharacterWindow.equipmentData[ slotNum ].id ) == GameData.TintMasks.B )
            then
                MakeOkayDialog( GetString( StringTables.Default.TEXT_DYE_MERCHANT_SECONDARY_TINT_ONLY ) )
            end
            
            return true
        end
    end
    
    local displayString = L""
    if( CharacterWindow.equipmentData[ slotNum ].id == 0 )
    then
        displayString = GetString( StringTables.Default.TEXT_DYE_MERCHANT_NO_ITEM_IN_SLOT )
    else
        displayString = GetString( StringTables.Default.TEXT_CANNOT_DYE_ITEM )
    end
    
    MakeOkayDialog( displayString )
    return false
end

local function GetNumDyedItems()
    local dyedA = 0
    local dyedB = 0
    
    for slot, item in ipairs( CharacterWindow.equipmentData )
    do
        if( item.id ~= 0 and item.flags[GameData.Item.EITEMFLAG_DYE_ABLE] )
        then
            if( item.dyeTintA ~= CharacterWindow.primaryColor )
            then
                dyedA = dyedA + 1
            end
            
            if( item.dyeTintB ~= CharacterWindow.secondaryColor )
            then
                dyedB = dyedB + 1
            end
        end
    end

    return dyedA, dyedB
end

function CharacterWindow.ShowDyeMerchant()
    local icon = CharacterWindow.TROPHY_EMPTY_ICON
    DynamicImageSetTexture( "DyeMerchantMarketingSlotIconBase", icon.texture, icon.x, icon.y )
    CharacterWindow.MarketingRButtonDown() -- this could be called with the window already opened to dye mode
    RevertAllDyePreview()
    
    
    WindowSetShowing("CharacterWindow", true)
    CharacterWindow.UpdateMode( CharacterWindow.MODE_DYE_MERCHANT )
end

function CharacterWindow.InitDyeMerchant()
    LabelSetText("DyeMerchantDyeAllLabel", GetString( StringTables.Default.LABEL_DYE_MERCHANT_DYE_ALL ) )
    LabelSetText("DyeMerchantCurrentColorDescLabel", GetString( StringTables.Default.LABEL_DYE_MERCHANT_MOUSE_OVER_COLOR ) )
    LabelSetText("DyeMerchantCostLabelTotal", GetString( StringTables.Default.LABEL_DYE_MERCHANT_TOTAL_COST ) )
    LabelSetText("CharacterWindowContentsInstructionText", GetString( StringTables.Default.TEXT_DYE_MERCHANT_INSTRUCTION ) )
    LabelSetText("DyeMerchantMarketingSlotDescLabel", GetString( StringTables.Default.TEXT_MARKETING_ITEM_SLOT_TIP ) )
end

function CharacterWindow.InitDyeMerchantButtons()
    LabelSetText("DyeMerchantButtonsCostLabelTotal", GetString( StringTables.Default.LABEL_DYE_MERCHANT_YOUR_MONEY ) )
    ButtonSetText("DyeMerchantButtonsAccept", GetString( StringTables.Default.LABEL_DYE_MERCHANT_ACCEPT ) )
    ButtonSetText("DyeMerchantButtonsCancel", GetString( StringTables.Default.LABEL_DYE_MERCHANT_CANCEL ) )
end

function CharacterWindow.HideDyeMerchant()
    if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        RevertAllDyePreview()
        CharacterWindow.Hide()
    end
end

function CharacterWindow.UpdateMode( mode )
    if( mode )
    then
        CharacterWindow.mode = mode
        if( mode == CharacterWindow.MODE_NORMAL )
        then
            CharacterWindow.dyeColors = nil

            if( not CharacterWindow.oldX or CharacterWindow.oldX > 545 )
            then
                CharacterWindow.oldX = 545
            end
            local _, y = WindowGetDimensions( "CharacterWindow" )
            WindowSetDimensions( "CharacterWindow", CharacterWindow.oldX, y )
            CharacterWindow.oldX = nil
            
            if( CharacterWindow.selectedDyeSlotName )
            then
                CharacterWindow.UnHightlightSlot( CharacterWindow.selectedDyeSlotName )
                CharacterWindow.selectedDyeSlotName = nil
            end
            
            WindowSetShowing( "CharacterWindowContents", true )
            WindowSetShowing( "CharacterWindowBrags", false )
            WindowSetShowing( "CharacterWindowTimeouts", false )

            WindowSetShowing( "CharacterWindowTabs", true )
            
            ButtonSetPressedFlag( "CharacterWindowTabsCharTab", true )
            ButtonSetStayDownFlag( "CharacterWindowTabsCharTab", true )
            ButtonSetPressedFlag( "CharacterWindowTabsBragsTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsBragsTab", false )
            ButtonSetPressedFlag( "CharacterWindowTabsTimeoutTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsTimeoutTab", false )
            
        elseif( mode == CharacterWindow.MODE_DYE_MERCHANT )
        then
            WindowSetShowing( "CharacterWindowContents", true )
            WindowSetShowing( "CharacterWindowBrags", false )
            WindowSetShowing( "CharacterWindowTimeouts", false )

            WindowSetShowing( "CharacterWindowTabs", false )
        
            CharacterWindow.primaryColor = -1
            CharacterWindow.secondaryColor = -1
            CharacterWindow.dyeColors = GetDyeMerchantData()
            CharacterWindow.dyeColorsLookUp = {}
            local bleachId = nil
            for index = 1, CharacterWindow.dyeColors.numColors
            do
                if( CharacterWindow.dyeColors[index].paletteIndex == 0 )
                then
                    bleachId = CharacterWindow.dyeColors[index].id
                end
                
                CharacterWindow.dyeColorsLookUp[ CharacterWindow.dyeColors[index].paletteIndex ] =  CharacterWindow.dyeColors[index]
            end
            
            ColorPickerCreateWithColorTable("DyeMerchantColorPicker", CharacterWindow.dyeColors, 5, 10, 10)
            if( bleachId )
            then
                local _, _, _, _, bleachX, bleachY = ColorPickerGetColorById( "DyeMerchantColorPicker", bleachId )
                local x, y = WindowGetOffsetFromParent( "DyeMerchantColorPicker" )
                WindowSetOffsetFromParent("DyeMerchantBleachSwatch", bleachX + x - 3, bleachY + y - 3)
            end
            
            WindowSetShowing( "DyeMerchantBleachSwatch", bleachId ~= nil )
            
            
            -- Set up anything else
            CharacterWindow.dyeAll = false
            ButtonSetCheckButtonFlag("DyeMerchantDyeAll", CharacterWindow.dyeAll )
            ButtonSetPressedFlag("DyeMerchantDyeAll", CharacterWindow.dyeAll )
            
            local x, y = WindowGetDimensions( "CharacterWindow" )
            local x2, _ = WindowGetDimensions( "DyeMerchant" )
            if( x > 545 )
            then
                x = 545
            end
            
            WindowSetDimensions( "CharacterWindow", x + x2, y )
            CharacterWindow.oldX = x
            
            -- Show the Dye merchant window
            EA_Window_InteractionBase.Hide()
            WindowSetShowing("DyeMerchantPrimary", false)
            WindowSetShowing("DyeMerchantSecondary", false)
            WindowSetShowing("DyeMerchantPrimarySecondary", false)
            MoneyFrame.FormatMoney ("DyeMerchantTotalCost", 0, false)
            MoneyFrame.FormatMoney ("DyeMerchantSingleCost", 0, false)
            MoneyFrame.FormatMoney ("DyeMerchantButtonsPlayerMoney", GameData.Player.money, false)
            WindowSetShowing( "DyeMerchantSingleCost", false )
            
        elseif( mode == CharacterWindow.MODE_BRAGS )
        then

            WindowSetShowing( "CharacterWindowContents", false )
            WindowSetShowing( "CharacterWindowBrags", true )
            WindowSetShowing( "CharacterWindowTimeouts", false )

            WindowSetShowing( "CharacterWindowTabs", true )
            
            ButtonSetPressedFlag( "CharacterWindowTabsCharTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsCharTab", false )
            ButtonSetPressedFlag( "CharacterWindowTabsBragsTab", true )
            ButtonSetStayDownFlag( "CharacterWindowTabsBragsTab", true )
            ButtonSetPressedFlag( "CharacterWindowTabsTimeoutTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsTimeoutTab", false )
        elseif( mode == CharacterWindow.MODE_TIMEOUTS )
        then

            WindowSetShowing( "CharacterWindowContents", false )
            WindowSetShowing( "CharacterWindowBrags", false )
            WindowSetShowing( "CharacterWindowTimeouts", true )

            WindowSetShowing( "CharacterWindowTabs", true )

            ButtonSetPressedFlag( "CharacterWindowTabsCharTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsCharTab", false )
            ButtonSetPressedFlag( "CharacterWindowTabsBragsTab", false )
            ButtonSetStayDownFlag( "CharacterWindowTabsBragsTab", false )
            ButtonSetPressedFlag( "CharacterWindowTabsTimeoutTab", true )
            ButtonSetStayDownFlag( "CharacterWindowTabsTimeoutTab", true )
        end
        
        local inDyeMode = mode == CharacterWindow.MODE_DYE_MERCHANT
        
        -- Show/Hide the Dye merchant window
        if( DoesWindowExist( "DyeMerchant" ) and DoesWindowExist( "DyeMerchantButtons" ) )
        then
            WindowSetShowing( "DyeMerchant", inDyeMode )
            WindowSetShowing( "DyeMerchantButtons", inDyeMode )
        end
        WindowSetShowing( "CharacterWindowContentsInstruction", inDyeMode )
        WindowSetShowing( "CharacterWindowContentsStatCombobox", not inDyeMode )
        WindowSetShowing( "CharacterWindowContentsComboboxBG", not inDyeMode )
    end
end

function CharacterWindow.UpdateMoney()
    if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        MoneyFrame.FormatMoney ("DyeMerchantButtonsPlayerMoney", GameData.Player.money, false)
    end
end

function CharacterWindow.OnMouseOverColorPicker( r, g, b, id )
    if( CharacterWindow.dyeColors )
    then
        -- Make a tool tip here
        local color = CharacterWindow.dyeColors[ GetIndexIntoDyeColors( id ) ]
        if( CharacterWindow.mouseOverColor ~= color )
        then
            local dyeName
            if( color == nil )
            then
                dyeName = L""
            elseif( color.paletteIndex == 0 ) -- bleach!
            then
                dyeName = GetString( StringTables.Default.TOOL_TIP_DYE_MERCHANT_BLEACH )
            else
                dyeName = GetDyeNameString( color.paletteIndex )
            end

            LabelSetText( "DyeMerchantCurrentColorLabel", dyeName )
            
            if( color )
            then
                MoneyFrame.FormatMoney ("DyeMerchantSingleCost", color.cost, false)
            end
            
            WindowSetShowing( "DyeMerchantSingleCost", color ~= nil )
            
            CharacterWindow.mouseOverColor = color
        end
    end
end

function CharacterWindow.OnMouseOverColorPickerEnd()
    LabelSetText( "DyeMerchantCurrentColorLabel", L"" )
    WindowSetShowing( "DyeMerchantSingleCost", false )
    CharacterWindow.mouseOverColor = nil
end

local function PreviewDyes()

    if( CharacterWindow.dyeAll )
    then
        DyeMerchantPreviewAll( CharacterWindow.primaryColor, CharacterWindow.secondaryColor )
    elseif( CharacterWindow.selectedDyeSlotName )
    then
        DyeMerchantPreview( GameData.ItemLocs.EQUIPPED, WindowGetId( CharacterWindow.selectedDyeSlotName ), CharacterWindow.primaryColor, CharacterWindow.secondaryColor )
    end
    
    -- Update Money
    local cost = 0
    local function GetDyeCost( dyeIndex )
        if( dyeIndex > -1 )
        then
            return CharacterWindow.dyeColorsLookUp[dyeIndex].cost
        end
        
        return 0
    end
    
    local aCost = GetDyeCost( CharacterWindow.primaryColor )
    local bCost = GetDyeCost( CharacterWindow.secondaryColor )
    if( CharacterWindow.dyeAll )
    then
        local numDyedA, numDyedB = GetNumDyedItems()
        cost = numDyedA * aCost + numDyedB * bCost
    else
        cost = aCost + bCost
    end
    
    MoneyFrame.FormatMoney ("DyeMerchantTotalCost", cost, false)
end

function CharacterWindow.OnColorPickerLButtonUp( flags, x, y )
    local color = ColorPickerGetColorAtPoint( "DyeMerchantColorPicker", x, y )
    local oldPrimary = CharacterWindow.primaryColor
    if( color )
    then
        local newColorIndex = CharacterWindow.dyeColors[ GetIndexIntoDyeColors( color.id ) ].paletteIndex
        if( newColorIndex == CharacterWindow.primaryColor )
        then
            newColorIndex = -1
        end
        
        CharacterWindow.primaryColor = newColorIndex
    else
        CharacterWindow.primaryColor = -1
    end
    
    if( CharacterWindow.primaryColor ~= -1 )
    then
        local selectedColorName = "DyeMerchantPrimary"
        local unselectedColorName = "DyeMerchantPrimarySecondary"
        if( CharacterWindow.primaryColor == CharacterWindow.secondaryColor )
        then
            selectedColorName = "DyeMerchantPrimarySecondary"
            unselectedColorName = "DyeMerchantPrimary"
            WindowSetShowing("DyeMerchantSecondary", false)
        elseif( CharacterWindow.secondaryColor ~= -1 )
        then
            WindowSetShowing("DyeMerchantSecondary", true)
        end
        
        local x, y = WindowGetOffsetFromParent( "DyeMerchantColorPicker" )
        WindowSetOffsetFromParent(selectedColorName, color.x + x - 7, color.y + y - 7)
        WindowSetOffsetFromParent(unselectedColorName, color.x + x - 7, color.y + y - 7)

        WindowSetShowing(selectedColorName, true)
        WindowSetShowing(unselectedColorName, false)
    else
        local selectedColorName = "DyeMerchantPrimary"
        if( oldPrimary ~= -1 and oldPrimary == CharacterWindow.secondaryColor )
        then
            selectedColorName = "DyeMerchantPrimarySecondary"
            WindowSetShowing("DyeMerchantSecondary", CharacterWindow.secondaryColor ~= -1)
        end
        
        WindowSetShowing(selectedColorName, false)
    end
    
    PreviewDyes()
end

function CharacterWindow.OnColorPickerRButtonUp( flags, x, y )
    local color = ColorPickerGetColorAtPoint( "DyeMerchantColorPicker", x, y )
    local oldSecondary = CharacterWindow.secondaryColor
    if( color )
    then
        local newColorIndex = CharacterWindow.dyeColors[ GetIndexIntoDyeColors( color.id ) ].paletteIndex
        if( newColorIndex == CharacterWindow.secondaryColor )
        then
            newColorIndex = -1
        end
        
        CharacterWindow.secondaryColor = newColorIndex
    else
        CharacterWindow.secondaryColor = -1
    end
    
    if( CharacterWindow.secondaryColor ~= -1 )
    then
        local selectedColorName = "DyeMerchantSecondary"
        local unselectedColorName = "DyeMerchantPrimarySecondary"
        if( CharacterWindow.primaryColor == CharacterWindow.secondaryColor )
        then
            selectedColorName = "DyeMerchantPrimarySecondary"
            unselectedColorName = "DyeMerchantSecondary"
            WindowSetShowing("DyeMerchantPrimary", false)
        elseif( CharacterWindow.primaryColor ~= -1 )
        then
            WindowSetShowing("DyeMerchantPrimary", true)
        end
        
        local x, y = WindowGetOffsetFromParent( "DyeMerchantColorPicker" )
        WindowSetOffsetFromParent(selectedColorName, color.x + x - 7, color.y + y - 7)
        WindowSetOffsetFromParent(unselectedColorName, color.x + x - 7, color.y + y - 7)

        WindowSetShowing(selectedColorName, true)
        WindowSetShowing(unselectedColorName, false)
    else
        local selectedColorName = "DyeMerchantSecondary"
        if( oldSecondary ~= -1 and oldSecondary == CharacterWindow.primaryColor )
        then
            selectedColorName = "DyeMerchantPrimarySecondary"
            WindowSetShowing("DyeMerchantPrimary", CharacterWindow.primaryColor ~= -1)
        end
        
        WindowSetShowing(selectedColorName, false)
    end
    
    PreviewDyes()
end

function CharacterWindow.OnAcceptDye()

    if( CharacterWindow.primaryColor == -1 )
    then
        GameData.DyeMerchant.tintA =  -1
    else
        GameData.DyeMerchant.tintA = CharacterWindow.dyeColorsLookUp[ CharacterWindow.primaryColor ].id
    end
    
    if( CharacterWindow.secondaryColor == -1 )
    then
        GameData.DyeMerchant.tintB =  -1
    else
        GameData.DyeMerchant.tintB = CharacterWindow.dyeColorsLookUp[ CharacterWindow.secondaryColor ].id
    end
    
    if( CharacterWindow.dyeAll )
    then
        BroadcastEvent( SystemData.Events.INTERACT_DYE_MERCHANT_DYE_ALL )
        CharacterWindow.pendingDyePreview = true
    elseif( CharacterWindow.selectedDyeSlotName )
    then
        GameData.DyeMerchant.slotNum = WindowGetId( CharacterWindow.selectedDyeSlotName )
        BroadcastEvent( SystemData.Events.INTERACT_DYE_MERCHANT_DYE_SINGLE )
    end
    
    if( CharacterWindow.marketItemData ~= nil ) 
    then
        -- 
        DialogManager.MakeTwoButtonDialog( GetString(StringTables.Default.TEXT_MARKETING_ITEM_CONFIRM), 
                                                   GetString(StringTables.Default.LABEL_YES), CharacterWindow.ApplyRewardItem, 
                                                   GetString(StringTables.Default.LABEL_NO) )
        return
    end
        
    
end

function CharacterWindow.OnCancelDye()
    RevertAllDyePreview()
    CharacterWindow.HideDyeMerchant()
end

function CharacterWindow.OnDyeAll()
    if( CharacterWindow.dyeAll )
    then
        RevertAllDyePreview()
        CharacterWindow.pendingDyePreview = true
    end
    
    local toggleChecked = not CharacterWindow.dyeAll
    ButtonSetCheckButtonFlag("DyeMerchantDyeAll", toggleChecked )
    ButtonSetPressedFlag("DyeMerchantDyeAll", toggleChecked )
    CharacterWindow.dyeAll = toggleChecked
    
    if( not CharacterWindow.pendingDyePreview )
    then
        PreviewDyes()
    end
end

-- this gets called during intitialization and for level up events in case a new slot becomes unlocked
function CharacterWindow.UnlockTrophies()
    
    local userLevel = GameData.Player.level
    if userLevel < 1 then
        CharacterWindow.numOfTrophiesUnlocked = 0
        return
    end
    
    CharacterWindow.numOfTrophiesUnlocked = math.min( math.floor(userLevel / 10) + 1, CharacterWindow.NUM_TROPHY_SLOTS )
    
    -- unlocked
    for trophyNum = 1, CharacterWindow.numOfTrophiesUnlocked do
        ButtonSetDisabledFlag( "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS), false )
        
        local text = GetStringFormat( StringTables.Default.LABEL_TROPHY, { trophyNum } )
        CharacterWindow.EquipmentSlotInfo[trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS] =  { name=text} 
        
        local lockIconWindowName = "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS).."LockIcon"
        WindowSetShowing( lockIconWindowName, false )
    end
    
    -- locked
    -- icon is now assembled from the untinted icon with a lock mini-icon on top
    for trophyNum = CharacterWindow.numOfTrophiesUnlocked+1, CharacterWindow.NUM_TROPHY_SLOTS do
    
        ButtonSetDisabledFlag( "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS), true )
        local windowName = "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS).."IconBase"
        local icon = CharacterWindow.TROPHY_EMPTY_ICON
        DynamicImageSetTexture( windowName, icon.texture, icon.x, icon.y)

        local requiredLevel = (trophyNum-1) * 10
        
        local text1 = GetStringFormat( StringTables.Default.LABEL_TROPHY, { trophyNum } )
        local text2 = GetStringFormat( StringTables.Default.LABEL_TROPHY_LOCKED, { requiredLevel } )
        CharacterWindow.EquipmentSlotInfo[trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS] =  { name=text1..L"\n"..text2 } 
        
        local lockIconWindowName = "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS).."LockIcon"
        lockIcon = CharacterWindow.TROPHY_LOCKED_ICON
        DynamicImageSetTexture( lockIconWindowName, lockIcon.texture, lockIcon.x, lockIcon.y)
        WindowSetShowing( lockIconWindowName, true )
        
        local miniIconWindowName = "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS).."MiniIcon"
        WindowSetShowing( miniIconWindowName, false )
    end
    
    CharacterWindow.UpdateSlotIcons()
end

-- provides the ItemInfo corresponding to the given slot
function CharacterWindow.GetItem( slot )

    if slot < GameData.Player.c_TROPHY_START_INDEX then
        return CharacterWindow.equipmentData[slot]

    else
        local trophySlot = slot - GameData.Player.c_TROPHY_START_INDEX + 1  
        return CharacterWindow.trophyData[trophySlot]
    end
end


function CharacterWindow.GetFirstAvailableTrophySlot( trophyData )

    for slot = 1, CharacterWindow.numOfTrophiesUnlocked do
        if CharacterWindow.trophyData[slot].uniqueID == 0 then
            return slot
        end
    end
    
    return CharacterWindow.INVALID_TROPHY_POSITION
end

function CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint )

    if attachPoint.inUse then
        return false
    end
    
    -- only need to check slot restrictions if the slotReqs table is set
    if trophyData.slots == nil or #trophyData.slots == 0 then
        return true
    end
    
    for i, slotAllowed in ipairs(trophyData.slots) do
        if attachPoint.inventorySlotNum == slotAllowed then
            return true     -- found as a valid slot
        end
    end
    
    return false        -- wasn't found in the valid slot list

end

-- if trophyData is nil then return any open attachment point
-- TODO: we need to limit this to valid locations for the passed in trophyData
--
-- if startIndex is set start search from that point
-- and wrap around the last index number to check 1 to startIndex-1
--
function CharacterWindow.GetNextAvailableAttachmentPoint( trophyData, startIndex )

    if CharacterWindow.trophyLocData == nil or #CharacterWindow.trophyLocData == 0 then
        return 0, nil
    end
    
    startIndex = startIndex or 1
    local attachPoint

    for index = startIndex, #CharacterWindow.trophyLocData do
        attachPoint = CharacterWindow.trophyLocData[index]
        if CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint ) then
            return index, attachPoint 
        end
    end
    
    for index = 1, (startIndex-1) do
        attachPoint = CharacterWindow.trophyLocData[index]
        if CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint ) then
            return index, attachPoint 
        end
    end

    return 0, nil
end


-- Does the reverse order search of CharacterWindow.GetNextAvailableAttachmentPoint 
--
-- if trophyData is nil then return any open attachment point
-- TODO: we need to limit this to valid locations for the passed in trophyData
--
-- if startIndex is set start search from that point
-- and wrap around the last index number to check 1 to startIndex-1
--
function CharacterWindow.GetPreviousAvailableAttachmentPoint( trophyData, startIndex )
    
    if CharacterWindow.trophyLocData == nil or #CharacterWindow.trophyLocData == 0 then
        return 0, nil
    end
    
    if startIndex < 1 then
        startIndex = #CharacterWindow.trophyLocData
    end
    
    startIndex = startIndex or #CharacterWindow.trophyLocData
    
    local attachPoint

    for index = startIndex, 1, -1 do
        attachPoint = CharacterWindow.trophyLocData[index]
        if CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint ) then
            return index, attachPoint 
        end
    end
    for index = #CharacterWindow.trophyLocData, (startIndex+1), -1 do
        attachPoint = CharacterWindow.trophyLocData[index]
        if CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint ) then
            return index, attachPoint 
        end
    end
    
    return 0, nil
end

function CharacterWindow.HightlightSlot( windowName )

    ButtonSetStayDownFlag( windowName, true )     
    ButtonSetPressedFlag( windowName, true )
end

function CharacterWindow.UnHightlightSlot( windowName )
    
    ButtonSetStayDownFlag( windowName, false )     
    ButtonSetPressedFlag( windowName, false )
end

function CharacterWindow.HideTrophyLocArrows()

    WindowSetShowing( "CharacterWindowPreviousButton", false )
    WindowSetShowing( "CharacterWindowNextButton", false )
    
    if CharacterWindow.currentlySelectedTrophyPosition ~= CharacterWindow.INVALID_TROPHY_POSITION then
        local previousWindow = "CharacterWindowContentsEquipmentSlot"..(CharacterWindow.currentlySelectedTrophyPosition+CharacterWindow.NUM_EQUIPMENT_SLOTS)
        CharacterWindow.UnHightlightSlot( previousWindow )
        CharacterWindow.currentlySelectedTrophyPosition = CharacterWindow.INVALID_TROPHY_POSITION 
    end 

    WindowUnregisterEventHandler( "CharacterWindow", SystemData.Events.L_BUTTON_DOWN_PROCESSED )
end


function CharacterWindow.ShowTrophyLocArrows( trophyNum )

    local previousWindow = "CharacterWindowContentsEquipmentSlot"..(CharacterWindow.currentlySelectedTrophyPosition+CharacterWindow.NUM_EQUIPMENT_SLOTS)
    CharacterWindow.UnHightlightSlot( previousWindow )
    
    -- trophy windows are grouped with other equipment slots
    local windowName = "CharacterWindowContentsEquipmentSlot"..(trophyNum+CharacterWindow.NUM_EQUIPMENT_SLOTS) 
    
    WindowClearAnchors( "CharacterWindowPreviousButton" )
    WindowAddAnchor( "CharacterWindowPreviousButton", "left", windowName, "right", -5, 0 )
    WindowSetShowing( "CharacterWindowPreviousButton", true )

    WindowClearAnchors( "CharacterWindowNextButton" )
    WindowAddAnchor( "CharacterWindowNextButton", "right", windowName, "left", 5, 0 )
    WindowSetShowing( "CharacterWindowNextButton", true )
    
    CharacterWindow.HightlightSlot( windowName )
    CharacterWindow.currentlySelectedTrophyPosition = trophyNum
    
    -- there is no way to see if already registered so we go ahead and unregister first to avoid the lua error from duplicate register
    WindowUnregisterEventHandler( "CharacterWindow", SystemData.Events.L_BUTTON_DOWN_PROCESSED )
    WindowRegisterEventHandler( "CharacterWindow", SystemData.Events.L_BUTTON_DOWN_PROCESSED, "CharacterWindow.OnLButtonDownProcessed") 
end

-- EventHandler for OnLButtonDownProcessed, which is only temporarily registered in order to deactivate 
--   the TrophyLocArrows when clicking off of them
--
function CharacterWindow.OnLButtonDownProcessed( )
    
    local windowName = SystemData.MouseOverWindow.name
    local currentTrophyWindowName = "CharacterWindowContentsEquipmentSlot"..(CharacterWindow.currentlySelectedTrophyPosition+CharacterWindow.NUM_EQUIPMENT_SLOTS)

    if windowName ~= nil and windowName ~= currentTrophyWindowName and 
       windowName ~= "CharacterWindowPreviousButton" and windowName ~= "CharacterWindowNextButton" then
        CharacterWindow.HideTrophyLocArrows()
        CharacterWindow.trophyDragStarted = false
    end

end

function CharacterWindow.NextButtonPressed()

    --TEMP FIX for GetLocationsForTrophies()  failing for unknown reasons
    if CharacterWindow.trophyLocData == nil then
        CharacterWindow.RerequestTrophyLocData()
    end
    --END TEMP FIX
    
    if CharacterWindow.currentlySelectedTrophyPosition == CharacterWindow.INVALID_TROPHY_POSITION then  
        -- this should never be true
        return
    end
    
    local trophyData = CharacterWindow.trophyData[CharacterWindow.currentlySelectedTrophyPosition]
    
    local startIndex = CharacterWindow.GetIndexForTrophyLoc( trophyData.trophyLocation, trophyData.trophyLocIndex )
    local index, attachPointData = CharacterWindow.GetNextAvailableAttachmentPoint( trophyData, startIndex+1 )
    
    if index ~= 0 then
        AttachTrophyToLocation( CharacterWindow.currentlySelectedTrophyPosition, attachPointData.trophyLocation, attachPointData.trophyLocIndex)
    end
end


function CharacterWindow.NextButtonMouseOver()

    Tooltips.CreateTextOnlyTooltip( "CharacterWindowNextButton", CharacterWindow.TOOLTIP_TROPHY_NEXT_LOC )
    Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT)
end


function CharacterWindow.PreviousButtonPressed()

    --TEMP FIX for GetLocationsForTrophies()  failing for unknown reasons
    if CharacterWindow.trophyLocData == nil then
        CharacterWindow.RerequestTrophyLocData()
    end
    --END TEMP FIX

    if CharacterWindow.currentlySelectedTrophyPosition == CharacterWindow.INVALID_TROPHY_POSITION then  
        -- this should never be true
        return
    end
    
    local trophyData = CharacterWindow.trophyData[CharacterWindow.currentlySelectedTrophyPosition]
    
    local startIndex = CharacterWindow.GetIndexForTrophyLoc( trophyData.trophyLocation, trophyData.trophyLocIndex )
    local index, attachPointData = CharacterWindow.GetPreviousAvailableAttachmentPoint( trophyData, startIndex-1 )
    if index ~= 0 then
        AttachTrophyToLocation( CharacterWindow.currentlySelectedTrophyPosition, attachPointData.trophyLocation, attachPointData.trophyLocIndex)
    end 
end

function CharacterWindow.PreviousButtonMouseOver()

    Tooltips.CreateTextOnlyTooltip( "CharacterWindowPreviousButton", CharacterWindow.TOOLTIP_TROPHY_PREV_LOC )
    Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_LEFT)
end


-- OnShutdown Handler
function CharacterWindow.Shutdown()
    WindowUnregisterEventHandler( "CharacterWindow", SystemData.Events.L_BUTTON_DOWN_PROCESSED )
    SetPaperDollBackground( "", 0, 0 )
end

-- OnShown Handler
function CharacterWindow.OnShown()
    CharacterWindow.UpdateTrophySlotIcons()
    if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        CharacterWindow.UpdateMode( CharacterWindow.MODE_NORMAL )
    end
    WindowUtils.OnShown(CharacterWindow.Hide, WindowUtils.Cascade.MODE_AUTOMATIC)
end

function CharacterWindow.ToggleShowing()   
    WindowUtils.ToggleShowing( "CharacterWindow" )
    local showing = WindowGetShowing( "CharacterWindow" )
    WindowSetShowing( "CharacterWindowTabs", showing )
end

function CharacterWindow.Hide()
    RevertAllDyePreview()
    WindowSetShowing("CharacterWindow", false)
    WindowSetShowing("CharacterWindowTabs", false)
end

function CharacterWindow.OnHidden()
    if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        RevertAllDyePreview()
        CharacterWindow.MarketingRButtonDown()
        CharacterWindow.UpdateMode( CharacterWindow.MODE_NORMAL )
    end
    CharacterWindow.HideTrophyLocArrows()
    WindowUtils.OnHidden()
end

function CharacterWindow.GetIndexForTrophyLoc( location, index )

    if CharacterWindow.trophyLocData == nil or #CharacterWindow.trophyLocData == 0 then
        return 0, nil
    end

    for apIndex, attachPoint in pairs( CharacterWindow.trophyLocData ) do
        if attachPoint.trophyLocation == location and attachPoint.trophyLocIndex == index then
            return apIndex, attachPoint
        end
    end
    
    return 0, nil
end


function CharacterWindow.UpdateSlotIcons()

    local texture, x, y  = 0, 0, 0
    local tint = CharacterWindow.NORMAL_TINT 

    CharacterWindow.equipmentData = DataUtils.GetEquipmentData()
    for equipmentData, slot in pairs(GameData.EquipSlots) do
        if( CharacterWindow.equipmentData[slot].iconNum ~= 0) then
            texture, x, y = GetIconData( CharacterWindow.equipmentData[slot].iconNum ) 
            tint = CharacterWindow.NORMAL_TINT
        else
            texture, x, y = GetIconData( CharacterWindow.EquipmentSlotInfo[slot].iconNum )  
        end     

        DynamicImageSetTexture( "CharacterWindowContentsEquipmentSlot"..slot.."IconBase", texture, x, y )
--        WindowSetTintColor( "CharacterWindowContentsEquipmentSlot"..slot, tint.R, tint.G, tint.B )
    end 

    CharacterWindow.UpdateTrophySlotIcons()
end 

function CharacterWindow.UpdateTrophySlotIcons()
    CharacterWindow.trophyLocData = GetLocationsForTrophies()   -- provides list of available attachment points
    CharacterWindow.trophyData = DataUtils.GetTrophyData()      -- provides item info for all trophy slots (may be blank data)
    CharacterWindow.numOfTrophiesEquipped = 0
    
    local trophyData, miniIconWindowName, index, attachPoint, validLocation, icon, texture, x, y, iconWindowName, availableIndex

    local unattachedTrophies = {}
    for  slot = 1, CharacterWindow.numOfTrophiesUnlocked  do  
        trophyData = CharacterWindow.trophyData[slot]
        
        -- trophy windows are grouped with other equipment slots
        miniIconWindowName = "CharacterWindowContentsEquipmentSlot"..(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS).."MiniIcon"	
		
        if( trophyData ~= nil and trophyData.uniqueID ~= 0) then
            texture, x, y = GetIconData( trophyData.iconNum ) 

            index, attachPoint = CharacterWindow.GetIndexForTrophyLoc( trophyData.trophyLocation, trophyData.trophyLocIndex )

            -- if trophy is not in a valid location, show mini icon and tooltip to say why invalid
            --
            validLocation = ( index ~= 0 and CharacterWindow.ValidLocationForTrophy( trophyData, attachPoint ) )
            WindowSetShowing( miniIconWindowName, not validLocation )
            if validLocation then -- trophy attached to a valid location

                attachPoint.inUse = true  -- mark it as not available for other trophies
                trophyData.tooltip = L""  -- tooltip only necessary for invalid location
            else
                -- Add to list of unattached trophies.
                -- We need to finish marking which ones are in use before we 
                --   can determine whether there are available slots for the unattached ones
                unattachedTrophies[slot] = trophyData
            end

            CharacterWindow.numOfTrophiesEquipped = CharacterWindow.numOfTrophiesEquipped + 1
        else

            -- display empty trophy slot icon
            icon = CharacterWindow.TROPHY_EMPTY_ICON
            texture, x, y = icon.texture, icon.x, icon.y

            WindowSetShowing( miniIconWindowName, false )
        end     
        
        iconWindowName = "CharacterWindowContentsEquipmentSlot"..(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS).."IconBase"
        DynamicImageSetTexture( iconWindowName, texture, x, y )
    end 

    -- we can't tell if anything is valid if retrieving CharacterWindow.trophyLocData failed, so may as well leave it with previous icons/tooltips until i 
    --if CharacterWindow.trophyLocData == nil then
    --    return
    --end
    
    -- now loop through the unattached trophies and provide the appropriate mini icon and tooltip
    for slot, trophyData in pairs(unattachedTrophies) do

        miniIconWindowName = "CharacterWindowContentsEquipmentSlot"..(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS).."MiniIcon"
        availableIndex = CharacterWindow.GetNextAvailableAttachmentPoint( trophyData )

        if availableIndex == 0 then
            -- there are no available locations for this trophy
            icon = CharacterWindow.TROPHY_NO_ATTACHMENT_POINT_ICON
            trophyData.tooltip = CharacterWindow.TOOLTIP_TROPHY_NO_AVAILABLE_LOC 
        else 
            -- the current set location is not available
            icon = CharacterWindow.TROPHY_INVALID_ATTACHMENT_POINT_ICON 
            trophyData.tooltip = CharacterWindow.TOOLTIP_TROPHY_INVALID_LOC 
        end
        
        DynamicImageSetTexture( miniIconWindowName, icon.texture, icon.x, icon.y )
    end

end

function CharacterWindow.RerequestTrophyLocData()

    CharacterWindow.trophyLocData = GetLocationsForTrophies()
    if CharacterWindow.trophyLocData == nil then
        DEBUG(L"CharacterWindow.RerequestTrophyLocData ERROR: still failed to receive trophy attachment point data")
        return false
    else 
        return true
    end
    
end


-- Creates a confirm dialog for BoE items,
-- which will then call the RequestMoveItem() if the player presses OK
function CharacterWindow.IsConfirmationNeededToMoveItem( Source, SourceSlot, DestSlot )

    if( Source == Cursor.SOURCE_INVENTORY )
    then
		
		local playerItems   = DataUtils.GetItems()
		local itemData      = playerItems[SourceSlot]
        if( itemData.uniqueID ~= 0 and itemData.flags[GameData.Item.EITEMFLAG_BIND_ON_EQUIP] and not itemData.boundToPlayer )
        then
            CharacterWindow.slotToEquip = DestSlot
            DialogManager.MakeTwoButtonDialog( GetString(StringTables.Default.TEXT_BIND_ON_EQUIP_CONFIRMATION), 
                                               GetString(StringTables.Default.LABEL_YES), CharacterWindow.RequestMoveItem, 
                                               GetString(StringTables.Default.LABEL_NO) )
            return true
        end
    end
    
    return false
end

-- OnLButtonDown Handler
function CharacterWindow.EquipmentLButtonDown( flags )

    local slot = WindowGetId(SystemData.ActiveWindow.name)
    if( Cursor.UseItemTargeting ) 
    then
        --Attempt to use the target item on the selected slot
        UseItemTargeting.HandleUseItemOnTarget(Cursor.SOURCE_EQUIPMENT, slot)
    
    elseif Cursor.IconOnCursor() 
    then
        -- MoveItem is handled on LButtonUp
		
    elseif( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        -- nothing yet
        
    else
                
        if CharacterWindow.equipmentData[slot].uniqueID ~= 0 
        then
            -- Create an Item Link on Shift-Left Click
            if( flags == SystemData.ButtonFlags.SHIFT)
            then
                EA_ChatWindow.InsertItemLink( CharacterWindow.equipmentData[slot] )
            else      
                Cursor.PickUp( Cursor.SOURCE_EQUIPMENT, slot, CharacterWindow.equipmentData[slot].uniqueID, CharacterWindow.equipmentData[slot].iconNum, true )           
                
                if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT ) 
                then
					CharacterWindow.UnHightlightSlot( "CharacterWindowContentsEquipmentSlot"..slot  )
                end
            end
        end
        
        CharacterWindow.dropPending = false
    end
end

function CharacterWindow.RequestMoveItem()
    if( CharacterWindow.slotToEquip and Cursor.IconOnCursor() )
    then
        -- Attempt to drop the object
        RequestMoveItem( Cursor.Data.Source, Cursor.Data.SourceSlot, Cursor.SOURCE_EQUIPMENT, CharacterWindow.slotToEquip, Cursor.Data.StackAmount )   
        CharacterWindow.dropPending = true
        CharacterWindow.slotToEquip = nil
    end
end


-- OnLButtonDown Handler
function CharacterWindow.TrophyLButtonDown()

    local slot = WindowGetId(SystemData.ActiveWindow.name)
    if Cursor.IconOnCursor() then
        
        local trophyIndex = slot + GameData.Player.c_TROPHY_START_INDEX - 1
        if (Cursor.Data.Source == Cursor.SOURCE_EQUIPMENT and Cursor.Data.SourceSlot == trophyIndex) then
			Cursor.Clear()
		end
		
    elseif CharacterWindow.trophyData[slot].uniqueID ~= 0 then  
        
        -- first click on the slot turns on trophy attachment buttons
        -- second click, needs to be done to cause pick up onto cursor
        -- click and hold should always pick up onto cursor
        if CharacterWindow.currentlySelectedTrophyPosition ~= slot then
            CharacterWindow.ShowTrophyLocArrows( slot )
        else
            local trophySlot = slot + GameData.Player.c_TROPHY_START_INDEX - 1  
            Cursor.PickUp( Cursor.SOURCE_EQUIPMENT, trophySlot, CharacterWindow.trophyData[slot].uniqueID, CharacterWindow.trophyData[slot].iconNum, true )   
            CharacterWindow.HideTrophyLocArrows()

        end

        CharacterWindow.dropPending = false  
    end
end

-- MouseDrag Handler on Trophy slots ( for drag & drop )
function CharacterWindow.TrophyDrag()

    -- have to skip the first TrophyDrag to wait and see if this is a single click (which also throws the OnDrag event)
    if not CharacterWindow.trophyDragStarted then
        CharacterWindow.trophyDragStarted = true
        return
    end

    local slot = WindowGetId(SystemData.ActiveWindow.name)
    if not Cursor.IconOnCursor() and CharacterWindow.trophyData[slot].uniqueID ~= 0 and CharacterWindow.currentlySelectedTrophyPosition == slot then

        CharacterWindow.trophyDragStarted = false
        local trophySlot = slot + GameData.Player.c_TROPHY_START_INDEX - 1  
        Cursor.PickUp( Cursor.SOURCE_EQUIPMENT, trophySlot, CharacterWindow.trophyData[slot].uniqueID, CharacterWindow.trophyData[slot].iconNum, true )   
        CharacterWindow.HideTrophyLocArrows()
    end

end

-- OnLButtonUp Handler ( Overload L Button up for drag & drop )
function CharacterWindow.EquipmentLButtonUp()
    local slot = WindowGetId(SystemData.ActiveWindow.name)
    
    if Cursor.IconOnCursor() and CharacterWindow.dropPending == false 
    then
        if CharacterWindow.IsConfirmationNeededToMoveItem( Cursor.Data.Source, Cursor.Data.SourceSlot, slot ) then
            return
        end
        -- Attempt to drop the object
        RequestMoveItem( Cursor.Data.Source, Cursor.Data.SourceSlot, Cursor.SOURCE_EQUIPMENT, slot, Cursor.Data.StackAmount )    
    
    elseif( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        local function SelectSlot()
            CharacterWindow.selectedDyeSlotName = SystemData.ActiveWindow.name
            
            --Highlight the slot
            CharacterWindow.HightlightSlot( CharacterWindow.selectedDyeSlotName )
        end
        
        local oldSelectedSlotName = CharacterWindow.selectedDyeSlotName
        local isSlotValid = IsSlotValidForDye( slot )
        if( oldSelectedSlotName )
        then
            CharacterWindow.UnHightlightSlot( oldSelectedSlotName )
            
            if( oldSelectedSlotName ~= SystemData.ActiveWindow.name and isSlotValid )
            then
                SelectSlot()
            else
                CharacterWindow.selectedDyeSlotName = nil
            end
            
            -- revert the stuff we had selected
            if( not CharacterWindow.dyeAll ) 
            then
                RevertDyePreview( GameData.ItemLocs.EQUIPPED, WindowGetId( oldSelectedSlotName ) )
            end
        elseif( isSlotValid ) -- nothing was selected before so select it
        then
            SelectSlot()
        else
            CharacterWindow.selectedDyeSlotName = nil
        end
        
        -- Do we need to preview anything?
        CharacterWindow.pendingDyePreview = CharacterWindow.selectedDyeSlotName ~= nil
    end
end


-- OnLButtonUp Handler ( Overload L Button up for drag & drop )
function CharacterWindow.TrophyLButtonUp()

    if( CharacterWindow.mode == CharacterWindow.MODE_DYE_MERCHANT )
    then
        DialogManager.MakeOneButtonDialog( GetString( StringTables.Default.TEXT_CANNOT_DYE_ITEM ), GetString( StringTables.Default.LABEL_OKAY ), nil )
        return
    end
    
    CharacterWindow.trophyDragStarted = false
    if Cursor.IconOnCursor() and CharacterWindow.dropPending == false then
        
        local slot = WindowGetId(SystemData.ActiveWindow.name)
        local trophyIndex = slot + GameData.Player.c_TROPHY_START_INDEX - 1
        local sourceWindow = Cursor.Data.Source
        local sourceSlot = Cursor.Data.SourceSlot
        
        -- don't need to move the item if it's dropped in same slot
        if (sourceWindow == Cursor.SOURCE_EQUIPMENT and sourceSlot == trophyIndex) then
            return
        end
		
        if CharacterWindow.IsConfirmationNeededToMoveItem( sourceWindow, sourceSlot, trophyIndex ) then
            return
        end
        
        -- TODO: should be able to automatically select attachment point when moving from other locations (e.g. bank), 
        --   but we don't have a function that can get the itemData from an unknown Cursor.Data.Source, so just auto attach
        --   if from Backpack, and can just move it otherwise.
        if sourceWindow == Cursor.SOURCE_INVENTORY then
            local itemData = DataUtils.GetItems ()[sourceSlot]
            CharacterWindow.AutoAttachTrophy( sourceWindow, sourceSlot, trophyIndex, itemData )
        else

            RequestMoveItem( sourceWindow, sourceSlot, Cursor.SOURCE_EQUIPMENT, trophyIndex, Cursor.Data.StackAmount ) 
        end
    end
end


-- OnRButtonDown Handler
function CharacterWindow.EquipmentRButtonDown()
    local slot = WindowGetId(SystemData.ActiveWindow.name)
    
    -- verify that we're clicking on an icon before spamming the server
    if not Cursor.IconOnCursor() and CharacterWindow.equipmentData[slot].uniqueID ~= 0 then

        -- Attempt to put this item back into the inventory
        RequestMoveItem( Cursor.SOURCE_EQUIPMENT, slot, Cursor.SOURCE_INVENTORY, CharacterWindow.FIRST_AVAILABLE_INVENTORY_SLOT, CharacterWindow.equipmentData[slot].stackCount)
    end
end


-- OnRButtonDown Handler
function CharacterWindow.TrophyRButtonDown()
    
    CharacterWindow.trophyDragStarted = false
    local slot = WindowGetId(SystemData.ActiveWindow.name)
    
    -- verify that we're clicking on an icon before spamming the server
    if not Cursor.IconOnCursor() and CharacterWindow.trophyData[slot].uniqueID ~= 0  then
    
        local trophyIndex = slot + GameData.Player.c_TROPHY_START_INDEX - 1
        RequestMoveItem( Cursor.SOURCE_EQUIPMENT, trophyIndex, Cursor.SOURCE_INVENTORY, CharacterWindow.FIRST_AVAILABLE_INVENTORY_SLOT, CharacterWindow.equipmentData[slot].stackCount)
        CharacterWindow.HideTrophyLocArrows()
    end
end


-- if itemData does not contain a valid attachment point
--   then try to auto attach to first available attachment point
-- This function also now handles the Request move since attaching 
--   at the same time as moving is a little tricky.
--  
function CharacterWindow.AutoAttachTrophy( sourceLocation, sourceSlot, trophySlot, itemData )
    
    if itemData == nil then
        return
    end
    
    local trophyLocation = GameData.Player.c_INVALID_TROPHY_LOCATION
    local trophyLocIndex = 0
    
    -- check if there is a previous attachment position (from being previously attached) and if that is still valid
    local apIndex, apData = CharacterWindow.GetIndexForTrophyLoc( itemData.trophyLocation, itemData.trophyLocIndex )
    if ( apIndex ~= 0 and CharacterWindow.ValidLocationForTrophy( itemData, apData ) ) then

        trophyLocation = apData.trophyLocation
        trophyLocIndex = apData.trophyLocIndex    
    else
    
        local index, attachPointData = CharacterWindow.GetNextAvailableAttachmentPoint( itemData )
        if index ~= 0 then
            trophyLocation = attachPointData.trophyLocation
            trophyLocIndex = attachPointData.trophyLocIndex
        end
    end
    
    RequestEquipTrophy( sourceLocation, sourceSlot, trophySlot, trophyLocation, trophyLocIndex )
end

-- note that this function may be called directly from the BackpackWindow
--
function CharacterWindow.AutoEquipItem( inventorySlot )

    -- retrieve inventorySlot's item data from BackpackWindow
    local invData = DataUtils.GetItems ()
    local itemData = invData[inventorySlot]

    if (itemData ~= nil and itemData.type == GameData.ItemTypes.TROPHY) then

        local trophySlot = CharacterWindow.GetFirstAvailableTrophySlot( itemData )
        if trophySlot == CharacterWindow.INVALID_TROPHY_POSITION then	
			-- if all slots filled, just swap with first trophy slot
			trophySlot = 1
		end

        local trophyIndex = trophySlot + GameData.Player.c_TROPHY_START_INDEX - 1
        CharacterWindow.AutoAttachTrophy( Cursor.SOURCE_INVENTORY, inventorySlot, trophyIndex, itemData )        
        
    else
        if( itemData.uniqueID ~= 0 and itemData.flags[GameData.Item.EITEMFLAG_BIND_ON_EQUIP] and not itemData.boundToPlayer )
        then
            DialogManager.MakeTwoButtonDialog( GetString(StringTables.Default.TEXT_BIND_ON_EQUIP_CONFIRMATION), 
                                                GetString(StringTables.Default.LABEL_YES), CharacterWindow.DialogAutoEquip, 
                                                GetString(StringTables.Default.LABEL_NO) )
            CharacterWindow.slotToEquip = inventorySlot
            return
        end
        AutoEquipItem( inventorySlot )
    end
end

function CharacterWindow.DialogAutoEquip()
    if( CharacterWindow.slotToEquip )
    then
        AutoEquipItem( CharacterWindow.slotToEquip ) 
        CharacterWindow.slotToEquip = nil
    end
end

-- SystemData.Events.PLAYER_EQUIPMENT_SLOT_UPDATED Handler
function CharacterWindow.UpdateEquipmentSlot( updatedSlots )

    CharacterWindow.UpdateSlotIcons()    
    
    for _, slot in ipairs( updatedSlots )
    do
        local uniqueID = 0
        if (CharacterWindow.equipmentData[slot] ~= nil)
        then
            uniqueID = CharacterWindow.equipmentData[slot].uniqueID
        end

        -- If we are placing the item that is currently on the cursor, clear it
        if( Cursor.IconOnCursor() and (Cursor.Data.ObjectId == uniqueID or CharacterWindow.dropPending == true) )
        then 
            Cursor.Clear()  
            CharacterWindow.dropPending = false
        end
        
        if( DataUtils.ItemHasEnhancementTimer( CharacterWindow.equipmentData[slot] ) )
        then
            table.insert( CharacterWindow.temporaryEnhancedItemsSlots, slot )
        end
        
        -- If we are mousing over the updated slot, show the tooltip
        if SystemData.MouseOverWindow.name == "CharacterWindowEquipmentSlot"..slot
        then
            CharacterWindow.MouseOverSlot( slot )
        end
    end

    CharacterWindow.UpdateStatsNew()
end

function CharacterWindow.ClearEnhancementTimer( slotToRemove )

    for i, slot in ipairs( CharacterWindow.temporaryEnhancedItemsSlots ) do
        if slot == slotToRemove then
            table.remove( CharacterWindow.temporaryEnhancedItemsSlots, i )
            return true
        end
    end
    
    return false
end


function CharacterWindow.OnUpdate( elapsedTime )
    if( CharacterWindow.pendingDyePreview )
    then
        PreviewDyes()
        CharacterWindow.pendingDyePreview = nil
    end
    
    CharacterWindow.UpdateTimeouts( elapsedTime )

    if( #CharacterWindow.temporaryEnhancedItemsSlots == 0 )
    then
        return
    end

    local timersToRemove = {}
    for index, slot in ipairs( CharacterWindow.temporaryEnhancedItemsSlots )
    do
        local item = CharacterWindow.equipmentData[slot]
        if( DataUtils.UpdateEnhancementTimer( item, elapsedTime ) )
        then
            table.insert( timersToRemove, slot )
        end
    end
    
    for index, slot in ipairs( timersToRemove )
    do
        CharacterWindow.ClearEnhancementTimer( slot )
    end
end

-- SystemData.Events.PLAYER_TROPHY_SLOT_UPDATED Handler
function CharacterWindow.UpdateTrophySlot( updatedSlots )
    
    -- We'll stick to this for now but as soon as possible we must switch to updating
    -- ONLY the slot with the concerned item.

    CharacterWindow.UpdateSlotIcons()

   for _, slot in ipairs( updatedSlots )
   do         
        local uniqueID = CharacterWindow.trophyData[slot].uniqueID
        -- If we are placing the item that is currently on the cursor, clear it
        if( Cursor.IconOnCursor() and (Cursor.Data.ObjectId == uniqueID or CharacterWindow.dropPending == true) ) then 
            Cursor.Clear()  
            CharacterWindow.dropPending = false
        end
        
        -- If we are mousing over the updated slot, show the tooltip
        if SystemData.MouseOverWindow.name == "CharacterWindowContentsEquipmentSlot"..(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS) then   
            
            CharacterWindow.TrophyMouseOverSlot( slot )
        end
    end

    CharacterWindow.UpdateStatsNew()
end


-- OnMouseOver Handler
function CharacterWindow.EquipmentMouseOver()

    CharacterWindow.MouseOverSlot( WindowGetId(SystemData.ActiveWindow.name) )
end 

function CharacterWindow.MouseOverSlot( slot )    
    
	local itemData = CharacterWindow.equipmentData[slot]
    if( itemData.uniqueID == 0 ) then        
        Tooltips.CreateTextOnlyTooltip( "CharacterWindowContentsEquipmentSlot"..slot, nil )
        Tooltips.SetTooltipText( 1, 1, CharacterWindow.EquipmentSlotInfo[slot].name )
        Tooltips.SetTooltipColor( 1, 1, 123, 172, 220 )
        Tooltips.Finalize()
        Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT )
        
    else
        Tooltips.CreateItemTooltip( itemData, "CharacterWindowContentsEquipmentSlot"..slot, Tooltips.ANCHOR_WINDOW_RIGHT, true )   
        if( DataUtils.ItemHasEnhancementTimer( itemData ) ) then
            Tooltips.SetUpdateCallback( Tooltips.ItemUpdateCallback )
        end
        
		if Cursor.UseItemTargeting then 
			UseItemTargeting.HandleMouseOverItem( itemData )
        end
    end
end 

-- OnMouseOverEnd Handler
function CharacterWindow.EquipmentMouseOverEnd()

	if Cursor.UseItemTargeting then 
		UseItemTargeting.HandleMouseOverItemEnd()
    end
end


-- OnMouseMove Handler
function CharacterWindow.TrophyMouseOver()
                            
    CharacterWindow.TrophyMouseOverSlot( WindowGetId(SystemData.ActiveWindow.name) )
end


function CharacterWindow.TrophyMouseOverSlot( slot )
    
    -- trophy windows are grouped with other equipment slots
    local windowName = "CharacterWindowContentsEquipmentSlot"..(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS) 
    
    if( CharacterWindow.trophyData[slot].uniqueID == 0 ) then     
       
        Tooltips.CreateTextOnlyTooltip( windowName, nil )
        Tooltips.SetTooltipText( 1, 1, CharacterWindow.EquipmentSlotInfo[(slot+CharacterWindow.NUM_EQUIPMENT_SLOTS)].name )
        Tooltips.SetTooltipColor( 1, 1, 123, 172, 220 )
        Tooltips.Finalize()
        Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT )
    else

        Tooltips.CreateItemTooltip( CharacterWindow.trophyData[slot], windowName, Tooltips.ANCHOR_WINDOW_RIGHT, true )   
    end
end 



-- TrophyMiniIconMouseOver is  commmented out because the tooltip on the mini icon 
--    is not working properly. It's repeatedly
--    firing OnMouseOver events rather than only sending it once.

function CharacterWindow.TrophyMiniIconMouseOver()

    local windowName = SystemData.ActiveWindow.name
    local slot = WindowGetId( WindowGetParent( windowName ) )
    CharacterWindow.TrophyMiniIconMouseOverSlot(windowName, slot)
end
    
function CharacterWindow.TrophyMiniIconMouseOverSlot(windowName, slot)
    
    local trophyData = CharacterWindow.trophyData[slot]
     
    local invalidTint = CharacterWindow.TROPHY_INVALID_LOC_TINT
    Tooltips.CreateTextOnlyTooltip( windowName, nil )
    Tooltips.SetTooltipText( 1, 1, trophyData.tooltip )
    Tooltips.SetTooltipColor( 1, 1, invalidTint.r, invalidTint.g, invalidTint.b )
    Tooltips.Finalize()
    Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT )
    
end

function CharacterWindow.UpdateHitPoints()
    local text = L""..GameData.Player.hitPoints.maximum
--    LabelSetText( "CharacterWindowHPValue", text )
end

function CharacterWindow.OnMouseOverStat(stat)
    CharacterWindow.CreateTooltip(SystemData.ActiveWindow.name, StatInfo[stat].name, StatInfo[stat].desc)
end

function CharacterWindow.OnMouseOverArmorValue()
    CharacterWindow.CreateTooltip(SystemData.ActiveWindow.name, GetString( StringTables.Default.LABEL_ARMOR ), GetString( StringTables.Default.TEXT_ARMOR ))
end

function CharacterWindow.OnMouseOverDPSValue()
    CharacterWindow.CreateTooltip(SystemData.ActiveWindow.name, GetString( StringTables.Default.LABEL_DAMAGE ), GetString( StringTables.Default.TEXT_DAMAGE ))
end

function CharacterWindow.OnMouseOverHPValue()       
    CharacterWindow.CreateTooltip(SystemData.ActiveWindow.name, GetString( StringTables.Default.LABEL_HIT_POINTS ), GetString( StringTables.Default.TEXT_HIT_POINTS ))
end

function CharacterWindow.UpdateCareerRank()
    
	LabelSetText( "CharacterWindowContentsImageTitleLabel", GetFormatStringFromTable("Default", StringTables.Default.LABEL_CHARACTER_WINDOW_RANK_X_CAREER, {GameData.Player.level, GameData.Player.career.name} ) )
    
    -- The player may have unlocked additional trophies.
    CharacterWindow.UnlockTrophies()
end 


function CharacterWindow.armorHasAttachmentPoint( trophyLocation, trophyLocIndex )
    
    local slot = CharacterWindow.GetIndexForTrophyLoc( trophyLocation, trophyLocIndex )
    
    if slot ~= 0 then
        return true 
    end
    
    return false
end

function CharacterWindow.UpdateStatCombobox()

    -- Clear ComboBox before populating to avoid duplicates
    ComboBoxClearMenuItems("CharacterWindowContentsStatCombobox")
    ComboBoxAddMenuItem("CharacterWindowContentsStatCombobox", GetString( StringTables.Default.LABEL_STATS ))
    ComboBoxAddMenuItem("CharacterWindowContentsStatCombobox", GetString( StringTables.Default.LABEL_DEFENSE ))
    ComboBoxAddMenuItem("CharacterWindowContentsStatCombobox", GetString( StringTables.Default.LABEL_MELEE ))
    ComboBoxAddMenuItem("CharacterWindowContentsStatCombobox", GetString( StringTables.Default.LABEL_RANGED ))
    ComboBoxAddMenuItem("CharacterWindowContentsStatCombobox", GetString( StringTables.Default.LABEL_MAGIC ))

    -- Reset selections
    CharacterWindow.currentStatSelection = 1
    CharacterWindow.previousStatSelection = 0
    ComboBoxSetSelectedMenuItem("CharacterWindowContentsStatCombobox", 1)
    CharacterWindow.UpdateStatsLabels()
end

function CharacterWindow.OnFilterSelChanged( curSel )
    CharacterWindow.currentStatSelection = curSel
    CharacterWindow.UpdateStatsNew()
end

function CharacterWindow.UpdateStatsNew()
    -- Always update the stat labels, since their values are stored and needed in the other labels
    CharacterWindow.UpdateStatsLabels()
    if CharacterWindow.currentStatSelection == 2 then
        CharacterWindow.UpdateDefenseLabels()
    elseif CharacterWindow.currentStatSelection == 3 then
        CharacterWindow.UpdateMeleeLabels()
    elseif CharacterWindow.currentStatSelection == 4 then
        CharacterWindow.UpdateRangedLabels()
    elseif CharacterWindow.currentStatSelection == 5 then
        CharacterWindow.UpdateMagicLabels()
    end

    CharacterWindow.UpdateStatIcons()
    CharacterWindow.previousStatSelection = CharacterWindow.currentStatSelection
end

function CharacterWindow.UpdateStatsLabels()
    CharacterWindow.UpdateStrengthLabel("CharacterWindowContentsStatsWindow1")
    CharacterWindow.UpdateBallisticskillLabel("CharacterWindowContentsStatsWindow2")
    CharacterWindow.UpdateIntelligenceLabel("CharacterWindowContentsStatsWindow3")
    CharacterWindow.UpdateToughnessLabel("CharacterWindowContentsStatsWindow4")
    CharacterWindow.UpdateWeaponskillLabel("CharacterWindowContentsStatsWindow5")
    CharacterWindow.UpdateInitiativeLabel("CharacterWindowContentsStatsWindow6")
    CharacterWindow.UpdateWillpowerLabel("CharacterWindowContentsStatsWindow7")
    CharacterWindow.UpdateWoundsLabel("CharacterWindowContentsStatsWindow8")
end

function CharacterWindow.UpdateDefenseLabels()
    CharacterWindow.UpdateArmorLabel("CharacterWindowContentsStatsWindow1")
    CharacterWindow.UpdateSpiritResistLabel("CharacterWindowContentsStatsWindow2")
    CharacterWindow.UpdateCorporealResistLabel("CharacterWindowContentsStatsWindow3")
    CharacterWindow.UpdateElementalResistLabel("CharacterWindowContentsStatsWindow4")
    CharacterWindow.UpdateBlockskillLabel("CharacterWindowContentsStatsWindow5")
    CharacterWindow.UpdateParryskillLabel("CharacterWindowContentsStatsWindow6")
    CharacterWindow.UpdateEvadeskillLabel("CharacterWindowContentsStatsWindow7")
    CharacterWindow.UpdateDisruptskillLabel("CharacterWindowContentsStatsWindow8")
end

function CharacterWindow.UpdateMeleeLabels()
    CharacterWindow.UpdateMeleeDPSLabel("CharacterWindowContentsStatsWindow1")
    CharacterWindow.UpdateMeleeBonusLabel("CharacterWindowContentsStatsWindow2")
    CharacterWindow.UpdateArmorPenetrationLabel("CharacterWindowContentsStatsWindow3")
    LabelSetText( "CharacterWindowContentsStatsWindow4Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow4Right", L"" )

    CharacterWindow.UpdateMeleeSpeedLabel("CharacterWindowContentsStatsWindow5")
    CharacterWindow.UpdateMeleeCritBonusLabel("CharacterWindowContentsStatsWindow6")
--    CharacterWindow.UpdateMeleeCritDamageLabel("CharacterWindowContentsStatsWindow7")
    LabelSetText( "CharacterWindowContentsStatsWindow7Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow7Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Right", L"" )
end

function CharacterWindow.UpdateRangedLabels()
    CharacterWindow.UpdateRangedDPSLabel("CharacterWindowContentsStatsWindow1")
    CharacterWindow.UpdateRangedSpeedLabel("CharacterWindowContentsStatsWindow2")
    CharacterWindow.UpdateRangedBonusLabel("CharacterWindowContentsStatsWindow3")
    CharacterWindow.UpdateRangedCritBonusLabel("CharacterWindowContentsStatsWindow4")
    --CharacterWindow.UpdateRangedCritDamageLabel("CharacterWindowContentsStatsWindow5")
    LabelSetText( "CharacterWindowContentsStatsWindow5Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow5Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow6Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow6Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow7Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow7Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Right", L"" )
end

function CharacterWindow.UpdateMagicLabels()
    CharacterWindow.UpdateSpellBonusLabel("CharacterWindowContentsStatsWindow1")
    CharacterWindow.UpdateSpellCritBonusLabel("CharacterWindowContentsStatsWindow2")
    CharacterWindow.UpdateSpellHealCritBonusLabel("CharacterWindowContentsStatsWindow3")
    --CharacterWindow.UpdateSpellCritDamageLabel("CharacterWindowContentsStatsWindow4")
    LabelSetText( "CharacterWindowContentsStatsWindow4Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow4Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow5Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow5Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow6Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow6Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow7Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow7Right", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Left", L"" )
    LabelSetText( "CharacterWindowContentsStatsWindow8Right", L"" )
end

function CharacterWindow.AdjustStatLabel(szLabelText)
    if (wstring.len(szLabelText) > 11) then
        szLabelText = wstring.sub(szLabelText, 0, 9)
        szLabelText = szLabelText..L"..."
    end
    return szLabelText
end

function CharacterWindow.UpdateTitleLabel()
    local activeTitle = L""
    if( GameData.Player.activeTitle ~= nil and GameData.Player.activeTitle ~= 0 ) then
        titleData = TomeGetPlayerTitleData(GameData.Player.activeTitle)
        if (titleData ~= nil) and (titleData.name ~= nil) then
            offset = wstring.find(titleData.name, L",")
            if (offset ~= nil) then
                activeTitle = wstring.sub(titleData.name, offset+1)
            else
                activeTitle = titleData.name
            end
            if (activeTitle == L"") or (activeTitle == nil) then
                activeTitle = L""
            end
        end
        CharacterWindow.currentTitleSelection = GameData.Player.activeTitle
    end

    LabelSetText( "CharacterWindowContentsImageTitleLabel", activeTitle )
end

function CharacterWindow.ShowCloak()
    local showCloak = ButtonGetPressedFlag("CharacterWindowContentsEquipmentShowCloak")
    SetEquippedItemVisible(GameData.EquipSlots.BACK, showCloak)
    
    if( showCloak )
    then
        ButtonSetDisabledFlag("CharacterWindowContentsEquipmentShowCloakHeraldry", false)
    else
        ButtonSetDisabledFlag("CharacterWindowContentsEquipmentShowCloakHeraldry", true)
    end
end

function CharacterWindow.ShowCloakHeraldry()
    CharacterWindow.showCloakHeraldry = ButtonGetPressedFlag("CharacterWindowContentsEquipmentShowCloakHeraldry")
    SystemData.UserInput.ChatText = L"/togglecloakheraldry"
    BroadcastEvent( SystemData.Events.SEND_CHAT_TEXT )
end

function CharacterWindow.ShowShowCloakOnly()
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloak", true)
    CharacterWindow.CreateTooltip("CharacterWindowContentsEquipmentShowCloak", GetString( StringTables.Default.TOOLTIP_SHOW_ITEM ) )
end

function CharacterWindow.ShowShowCloakHeraldryOnly()
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloakHeraldry", true)
    CharacterWindow.CreateTooltip("CharacterWindowContentsEquipmentShowCloakHeraldry", GetString( StringTables.Default.TOOLTIP_SHOW_HERALDRY ) )
end

function CharacterWindow.ShowCloakOptions()
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloak", true)
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloakHeraldry", true)
    CharacterWindow.EquipmentMouseOver()
end

function CharacterWindow.HideCloakOptions()
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloak", false)
    WindowSetShowing("CharacterWindowContentsEquipmentShowCloakHeraldry", false)
    CharacterWindow.EquipmentMouseOverEnd()
end

function CharacterWindow.ShowHelm()
    local showHelm = ButtonGetPressedFlag("CharacterWindowContentsEquipmentShowHelm")
    SetEquippedItemVisible(GameData.EquipSlots.HELM, showHelm)
end

function CharacterWindow.ShowShowHelmOnly()
    WindowSetShowing("CharacterWindowContentsEquipmentShowHelm", true)
    CharacterWindow.CreateTooltip("CharacterWindowContentsEquipmentShowHelm", GetString( StringTables.Default.TOOLTIP_SHOW_ITEM ) )
end

function CharacterWindow.ShowShowHelm()
    WindowSetShowing("CharacterWindowContentsEquipmentShowHelm", true)
    CharacterWindow.EquipmentMouseOver()
end

function CharacterWindow.HideShowHelm()
    WindowSetShowing("CharacterWindowContentsEquipmentShowHelm", false)
    CharacterWindow.EquipmentMouseOverEnd()
end

function CharacterWindow.MouseOverDone(index)
    if CharacterWindow.currentStatSelection == 1 then
        return CharacterWindow.MouseOverStatsDone(index)
    elseif CharacterWindow.currentStatSelection == 2 then
-- Removed the offensive stats so the defensive ones are #2
--        return CharacterWindow.MouseOverOffenseDone(index)
--    elseif CharacterWindow.currentStatSelection == 3 then
        return CharacterWindow.MouseOverDefenseDone(index)
    end
end

function CharacterWindow.MouseOverStatsDone(index)
    if (1 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.STRENGTH)
        return true
    elseif (2 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.TOUGHNESS)
        return true
    elseif (3 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.WOUNDS)
        return true
    elseif (4 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.INITIATIVE)
        return true
    elseif (5 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.WEAPONSKILL)
        return true
    elseif (6 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.BALLISTICSKILL)
        return true
    elseif (7 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.INTELLIGENCE)
        return true
    elseif (8 == index) then
        CharacterWindow.OnMouseOverStat(GameData.Stats.WILLPOWER)
        return true
    end

    return false
end

function CharacterWindow.MouseOverOffenseDone(index)
    if (1 == index) then
        return false
    elseif (2 == index) then
        return false
    elseif (3 == index) then
        return false
    elseif (4 == index) then
        return false
    elseif (5 == index) then
        return false
    elseif (6 == index) then
        return false
    elseif (7 == index) then
        return false
    elseif (8 == index) then
        return false
    end
    
    return false
end

function CharacterWindow.MouseOverDefenseDone(index)
    if (1 == index) then
        CharacterWindow.OnMouseOverArmorValue()
        return true
    elseif (2 == index) then
        return false
    elseif (3 == index) then
        return false
    elseif (4 == index) then
        return false
    elseif (5 == index) then
        return false
    elseif (6 == index) then
        return false
    elseif (7 == index) then
        return false
    elseif (8 == index) then
        return false
    end
    
    return false
end

function CharacterWindow.StatsMouseOver()
    local windowID = WindowGetId(SystemData.ActiveWindow.name)
    
    CharacterWindow.CreateTooltip("CharacterWindowContentsStatsWindow"..windowID, CharacterWindow.GetStatsToolTip(windowID), CharacterWindow.GetStatsToolTipLine2(windowID) )
end

function CharacterWindow.GetStatsToolTip(index)
    if CharacterWindow.currentStatSelection == 1 then
        return CharacterWindow.GetStatsLabelsTooltip(index)
    elseif CharacterWindow.currentStatSelection == 2 then
        return CharacterWindow.GetDefenseLabelsTooltip(index)
    elseif CharacterWindow.currentStatSelection == 3 then
        return CharacterWindow.GetMeleeLabelsTooltip(index)
    elseif CharacterWindow.currentStatSelection == 4 then
        return CharacterWindow.GetRangedLabelsTooltip(index)
    elseif CharacterWindow.currentStatSelection == 5 then
        return CharacterWindow.GetMagicLabelsTooltip(index)
    end
    
    return
end

function CharacterWindow.GetStatsToolTipLine2(index)
    if CharacterWindow.currentStatSelection == 1 then
        return CharacterWindow.GetStatsLabelsTooltipLine2(index)
    elseif CharacterWindow.currentStatSelection == 2 then
        return CharacterWindow.GetDefenseLabelsTooltipLine2(index)
    elseif CharacterWindow.currentStatSelection == 3 then
        return CharacterWindow.GetMeleeLabelsTooltipLine2(index)
    elseif CharacterWindow.currentStatSelection == 4 then
        return CharacterWindow.GetRangedLabelsTooltipLine2(index)
    elseif CharacterWindow.currentStatSelection == 5 then
        return CharacterWindow.GetMagicLabelsTooltipLine2(index)
    end
    
    return
end

function CharacterWindow.GetStatsLabelsTooltip(index)
    local retString = L""
    if (1 == index) then
        retString = GetString( StringTables.Default.LABEL_STRENGTH )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.STRENGTH, g_currentStrength)
        return retString
    elseif (2 == index) then
        retString = GetString( StringTables.Default.LABEL_BALLISTICSKILL )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.BALLISTICSKILL, g_currentBallisticskill)
        return retString
    elseif (3 == index) then
        retString = GetString( StringTables.Default.LABEL_INTELLIGENCE )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.INTELLIGENCE, g_currentIntelligence)
        return retString
    elseif (4 == index) then
        retString = GetString( StringTables.Default.LABEL_TOUGHNESS )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.TOUGHNESS, g_currentToughness)
        return retString
    elseif (5 == index) then
        retString = GetString( StringTables.Default.LABEL_WEAPONSKILL )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.WEAPONSKILL, g_currentWeaponskill)
        return retString
    elseif (6 == index) then
        retString = GetString( StringTables.Default.LABEL_INITIATIVE )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.INITIATIVE, g_currentInitiative)
        return retString
    elseif (7 == index) then
        retString = GetString( StringTables.Default.LABEL_WILLPOWER )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.WILLPOWER, g_currentWillpower)
        return retString
    elseif (8 == index) then
        retString = GetString( StringTables.Default.LABEL_WOUNDS )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.WOUNDS, g_currentWounds)
        return retString
    end
    
    return
end

function CharacterWindow.GetDefenseLabelsTooltip(index)
    local retString = L""
    if (1 == index) then
        retString = GetString( StringTables.Default.LABEL_ARMOR )
        retString = retString..L": "
        retString = retString..GameData.Player.armorValue
        return retString
    elseif (2 == index) then
        retString = GetString( StringTables.Default.LABEL_SPIRITRESIST )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.SPIRITRESIST, g_currentSpirit)
        return retString
    elseif (3 == index) then
        retString = GetString( StringTables.Default.LABEL_CORPOREALRESIST )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.CORPOREALRESIST, g_currentCorporeal)
        return retString
    elseif (4 == index) then
        retString = GetString( StringTables.Default.LABEL_ELEMENTALRESIST )
        retString = retString..CharacterWindow.GetBasicStatTooltip(GameData.Stats.ELEMENTALRESIST, g_currentElemental)
        return retString
    elseif (5 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS_BLOCK )
        retString = retString..CharacterWindow.GetBasicStatPercentageTooltip(GameData.Stats.BLOCKSKILL, g_currentBlock)
        return retString
    elseif (6 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS_PARRY )
        retString = retString..CharacterWindow.GetBasicStatPercentageTooltip(GameData.Stats.PARRYSKILL, g_currentParry)
        return retString
    elseif (7 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS_EVADE )
        retString = retString..CharacterWindow.GetBasicStatPercentageTooltip(GameData.Stats.EVADESKILL, g_currentEvade)
        return retString
    elseif (8 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS_DISRUPT )
        retString = retString..CharacterWindow.GetBasicStatPercentageTooltip(GameData.Stats.DISRUPTSKILL, g_currentDisrupt)
        return retString
    end
    
    return
end

function CharacterWindow.GetStrengthTooltipDesc()
    local AbilDam = g_currentStrength/5
    local AutoDam = g_currentStrength/10
    local Params = {wstring.format(L"%.01f",AbilDam), wstring.format(L"%.01f",AutoDam)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_STRENGTH_DESC, Params )
end

function CharacterWindow.GetBallisticSkillTooltipDesc()
    local AbilDam = g_currentBallisticskill/5
    local AutoDam = g_currentBallisticskill/10
    local Params = {wstring.format(L"%.01f",AbilDam), wstring.format(L"%.01f",AutoDam)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_BALLISTICSKILL_DESC, Params )
end

function CharacterWindow.GetIntelligenceTooltipDesc()
    local AbilDam = g_currentIntelligence/5
    local Params = {wstring.format(L"%.01f",AbilDam)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_INTELLIGENCE_DESC, Params )
end

function CharacterWindow.GetToughnessTooltipDesc()
    local DamReduc = g_currentToughness/5
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_TOUGHNESS_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

function CharacterWindow.GetWeaponSkillTooltipDesc()
    local ArmorPen = g_currentWeaponskill/(GameData.Player.battleLevel * 7.5 + 50) * .25
    local ArmorPen = ArmorPen * 100
    local Params = {wstring.format(L"%.01f",ArmorPen)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_WEAPONSKILL_DESC, Params )
end

function CharacterWindow.GetInitiativeTooltipDesc()
    local CritHit = (GameData.Player.battleLevel * 7.5 + 50)/g_currentInitiative * .1
    local CritHit = CritHit * 100
    CritHit = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_REDUCTION, CritHit )
    local Params = {wstring.format(L"%.01f",CritHit)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_INITIATIVE_DESC, Params )
end

function CharacterWindow.GetWillPowerTooltipDesc()
    local AbilDam = g_currentWillpower/5
    local Params = {wstring.format(L"%.01f",AbilDam)}
    return GetFormatStringFromTable("Default", StringTables.Default.TEXT_WILLPOWER_DESC, Params )
end

function CharacterWindow.GetWoundsTooltipDesc()
    local DamReduc = g_currentWounds * 10
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_WOUNDS_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

function CharacterWindow.GetStatsLabelsTooltipLine2(index)
    if (1 == index) then
        return CharacterWindow.GetStrengthTooltipDesc()
    elseif (2 == index) then
        return CharacterWindow.GetBallisticSkillTooltipDesc()
    elseif (3 == index) then
        return CharacterWindow.GetIntelligenceTooltipDesc()
    elseif (4 == index) then
        return CharacterWindow.GetToughnessTooltipDesc()
    elseif (5 == index) then
        return CharacterWindow.GetWeaponSkillTooltipDesc()
    elseif (6 == index) then
        return CharacterWindow.GetInitiativeTooltipDesc()
    elseif (7 == index) then
        return CharacterWindow.GetWillPowerTooltipDesc()
    elseif (8 == index) then
        return CharacterWindow.GetWoundsTooltipDesc()
    end
    
    return
end

function CharacterWindow.GetArmorTooltipDesc()
    local DamReduc = GameData.Player.armorValue / (GameData.Player.battleLevel * 44) * .4
    local DamReduc = DamReduc * 100
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_ARMOR_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

-- Calculate the value of the stat after diminishing returns has been applied to the current value,
-- this function tries to mimic the servers behavior
local function GetEffectiveStatValue( baseValue, currentValue )
    local thresholdMult = 25
    local threshold = GameData.Player.battleLevel * thresholdMult + 50
    local capMult = 40
    local cap = GameData.Player.battleLevel * capMult + 50

    -- After the dimishing returns kick in, we only get half the effectiveness from stats
    if( currentValue > threshold )
    then
        currentValue = ( currentValue - threshold ) / 2 + threshold
    end
    
    -- An absolute stat cap.... terrible design, but theoretically only happens if items are being created with bad parameters
    if( currentValue > cap )
    then
        currentValue = cap
    end
    
    if( baseValue > currentValue )
    then
        baseValue = currentValue
    end
    
    -- Truncate for nicer output
    baseValue = math.floor( baseValue )
    currentValue = math.floor( currentValue ) 
    
    return baseValue, currentValue
end

function CharacterWindow.IsStatDiminished( currentValue )
    local thresholdMult = 25
    local threshold = GameData.Player.battleLevel * thresholdMult + 50
    if( currentValue > threshold )
    then
        return true
    end
    return false
end

-- Calculates the reduction in damage from the current resist stat value, takes diminishing returns in account
-- this function tries to mimic the servers behavior
local function GetEffectiveResistValue( defense )
    local attackLevelCoeff = GameData.Player.battleLevel * 8.4
    
    local defenseBeforeSoftCap = 0.4 / 0.2 * attackLevelCoeff
	if ( defense > defenseBeforeSoftCap )
	then
		-- Diminishing returns
		defense = ( defense - defenseBeforeSoftCap ) / 3.0 + defenseBeforeSoftCap
	end

	if ( attackLevelCoeff == 0 )
	then
		attackLevelCoeff = 1.0
	end
	
	local DamReduc = Clamp( 0.2 * ( defense / attackLevelCoeff ), 0.0, 0.75 );
	return DamReduc * 100
end

-- Checks if resist value is above threshold for dimishing returns
function CharacterWindow.IsResistDiminished( currentValue )
	local attackLevelCoeff = GameData.Player.battleLevel * 8.4
	local defenseBeforeSoftCap = 0.4 / 0.2 * attackLevelCoeff
	if ( currentValue > defenseBeforeSoftCap )
	then
		return true
	end
	return false
end

function CharacterWindow.GetSpiritResistTooltipDesc()
    local DamReduc = GetEffectiveResistValue( g_currentSpirit )  
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_SPIRITRESIST_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

function CharacterWindow.GetCorporealResistTooltipDesc()
    local DamReduc = GetEffectiveResistValue( g_currentCorporeal )  
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_CORPOREALRESIST_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

function CharacterWindow.GetElementalResistTooltipDesc()
    local DamReduc = GetEffectiveResistValue( g_currentElemental )  
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_ELEMENTALRESIST_DESC, {wstring.format(L"%.01f",DamReduc)} )
end

function CharacterWindow.GetBlockTooltipDesc(index)
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_BONUS_BLOCK_DESC, {wstring.format(L"%.01f",g_currentBlock)} )
end

function CharacterWindow.GetParryTooltipDesc(index)
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_BONUS_PARRY_DESC, {wstring.format(L"%.01f",g_currentParry)} )
end

function CharacterWindow.GetEvadeTooltipDesc(index)
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_BONUS_EVADE_DESC, {wstring.format(L"%.01f",g_currentEvade)} )
end

function CharacterWindow.GetDisruptTooltipDesc(index)
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_BONUS_DISRUPT_DESC, {wstring.format(L"%.01f",g_currentDisrupt)} )
end

function CharacterWindow.GetDefenseLabelsTooltipLine2(index)
    if (1 == index) then
        return CharacterWindow.GetArmorTooltipDesc()
    elseif (2 == index) then
        return CharacterWindow.GetSpiritResistTooltipDesc()
    elseif (3 == index) then
        return CharacterWindow.GetCorporealResistTooltipDesc()
    elseif (4 == index) then
        return CharacterWindow.GetElementalResistTooltipDesc()
    elseif (5 == index) then
        return CharacterWindow.GetBlockTooltipDesc(index)
    elseif (6 == index) then
        return CharacterWindow.GetParryTooltipDesc(index)
    elseif (7 == index) then
        return CharacterWindow.GetEvadeTooltipDesc(index)
    elseif (8 == index) then
        return CharacterWindow.GetDisruptTooltipDesc(index)
    end
    
    return
end

function CharacterWindow.GetMeleeLabelsTooltip(index)
    local retString = L""
    if (1 == index) then
        retString = CharacterWindow.GetMeleeDPSLabelLeft(false)
        retString = retString..CharacterWindow.GetMeleeDPSLabelRight()
        return retString
    elseif (2 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS )..L":"..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_MELEE, 0)
        return retString
    elseif (3 == index) then
        retString = GetString( StringTables.Default.LABEL_MELEE_ARMOR_PENETRATION )..L":"..CharacterWindow.CalcArmorPenetration()..L"%"
        return retString
    elseif (4 == index) then
        return
    elseif (5 == index) then
        retString = GetString( StringTables.Default.LABEL_SPEED )..L":"..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RIGHT_HAND].speed)
        return retString
    elseif (6 == index) then
        local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MELEE, 0)
        critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
        retString = GetString( StringTables.Default.LABEL_CRIT_BONUS )..L":"..critBonus..L"%"
        return retString
    elseif (7 == index) then
 --[[       local critBaseDamage = CharacterWindow.GetBaseCritDamage( L"melee" )
        local critBonusTotal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_DAMAGE, critBaseDamage )
        critBonusTotal = Clamp( critBonusTotal, 5.0, 75.0 )
        retString = GetString( StringTables.Default.LABEL_CRIT_DMG )..L":"..wstring.format(L"%d", critBonusTotal)..L"%"
        return retString
--]]    return
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetMeleeDPSTooltipDesc()
    local Params = {wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RIGHT_HAND].dps), wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.LEFT_HAND].dps)}
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_MELEE_WEAPON_DPS_DESC, Params )
end

function CharacterWindow.GetDamageBonusTooltipDesc(bonusType, szAttackType)
    local bonusVal = CharacterWindow.CalculateValueWithBonus(bonusType, 0)
    local abilDam = GetAbilityDamageFromStat( bonusVal ) + 0.5
    local Params = {szAttackType, wstring.format(L"%.01f", bonusVal/5), wstring.format(L"%.01f", bonusVal/10)}
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_BONUS_DESC, Params )
end

function CharacterWindow.GetArmorPenetrationTooltipDesc()
    local Params = {wstring.format(L"%.01f",CharacterWindow.CalcArmorPenetration())}
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_MELEE_ARMORE_PENETRATION_DESC, Params )
end

function CharacterWindow.GetSpeedTooltipDesc()
    return L""
end

function CharacterWindow.GetCritBonusTooltipDesc(bonusType, szAttackType)
    local critBonusGeneral = CharacterWindow.CalculateValueWithBonus(bonusType, 0)
    local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonusGeneral)
    local Params = {szAttackType, wstring.format(L"%.01f", critBonus)}
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_CRIT_BONUS_DESC, Params )
end

function CharacterWindow.GetCritDamageTooltipDesc( attackType )
    local critBaseDamage = CharacterWindow.GetBaseCritDamage( attackType )
    critBaseDamage = Clamp( critBaseDamage, 5.0, 75.0 )  
    local effectiveBonus = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_DAMAGE, critBaseDamage )
    local critBonusDamage = 100 * effectiveBonus / critBaseDamage
    local Params = { attackType, wstring.format( L"%d", critBaseDamage ), wstring.format( L"%d", critBonusDamage ) }
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_CRIT_DMG_DESC, Params )
end

function CharacterWindow.GetMeleeLabelsTooltipLine2(index)
    if (1 == index) then
        return CharacterWindow.GetMeleeDPSTooltipDesc()
    elseif (2 == index) then
        return CharacterWindow.GetDamageBonusTooltipDesc(GameData.BonusTypes.EBONUS_DAMAGE_MELEE, GetString( StringTables.Default.LABEL_MELEE ))
    elseif (3 == index) then
        return CharacterWindow.GetArmorPenetrationTooltipDesc()
    elseif (4 == index) then
        return
    elseif (5 == index) then
        return CharacterWindow.GetSpeedTooltipDesc()
    elseif (6 == index) then
        return CharacterWindow.GetCritBonusTooltipDesc(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MELEE, GetString( StringTables.Default.LABEL_ATTACK ) )
    elseif (7 == index) then
        return --CharacterWindow.GetCritDamageTooltipDesc( L"melee" )
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetRangedLabelsTooltip(index)
    local retString = L""
    if (1 == index) then
        retString = GetString( StringTables.Default.LABEL_WEAPON_DPS )..L":"..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].dps)
        return retString
    elseif (2 == index) then
        retString = GetString( StringTables.Default.LABEL_SPEED )..L":"..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].speed)
        return retString
    elseif (3 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS )..L":"..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_RANGED, 0)
        return retString
    elseif (4 == index) then
        local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_RANGED, 0)
        critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
        retString = GetString( StringTables.Default.LABEL_CRIT_BONUS )..L":"..critBonus..L"%"
        return retString
    elseif (5 == index) then
--[[        local critBaseDamage = CharacterWindow.GetBaseCritDamage( L"ranged" )
        local critBonusTotal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_DAMAGE, critBaseDamage )        
        critBonusTotal = Clamp( critBonusTotal, 5.0, 75.0 )
        retString = GetString( StringTables.Default.LABEL_CRIT_DMG )..L":"..wstring.format(L"%d", critBonusTotal)..L"%"
        return retString
--]]
        return
    elseif (6 == index) then
        return
    elseif (7 == index) then
        return
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetRangedDPSTooltipDesc()
    local Params = {wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].dps)}
    return GetFormatStringFromTable( "Default", StringTables.Default.TEXT_RANGED_WEAPON_DPS_DESC, Params )
end

function CharacterWindow.GetRangedLabelsTooltipLine2(index)
    if (1 == index) then
        return CharacterWindow.GetRangedDPSTooltipDesc()
    elseif (2 == index) then
        return CharacterWindow.GetSpeedTooltipDesc()
    elseif (3 == index) then
        return CharacterWindow.GetDamageBonusTooltipDesc(GameData.BonusTypes.EBONUS_DAMAGE_RANGED, GetString( StringTables.Default.LABEL_RANGED ))
    elseif (4 == index) then
        return CharacterWindow.GetCritBonusTooltipDesc(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_RANGED, GetString( StringTables.Default.LABEL_ATTACK ) )
    elseif (5 == index) then
        return --CharacterWindow.GetCritDamageTooltipDesc( L"ranged" )
    elseif (6 == index) then
        return
    elseif (7 == index) then
        return
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetMagicLabelsTooltip(index)
    local retString = L""
    if (1 == index) then
        retString = GetString( StringTables.Default.LABEL_BONUS )..L":"..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_MAGIC, 0)
        return retString
    elseif (2 == index) then
        local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MAGIC, 0)
        critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
        retString = GetString( StringTables.Default.LABEL_CRIT_BONUS )..L":"..critBonus..L"%"
        return retString
    elseif (3 == index) then
        local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_HEALING, 0)
        critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
        retString = GetString( StringTables.Default.LABEL_CRIT_BONUS )..L":"..critBonus..L"%"
        return retString
    elseif (4 == index) then
--[[        local critBaseDamage = CharacterWindow.GetBaseCritDamage( L"spell" )
        local critBonusTotal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_DAMAGE, critBaseDamage )
        critBonusTotal = Clamp( critBonusTotal, 5.0, 75.0 )        
        retString = GetString( StringTables.Default.LABEL_CRIT_DMG )..L":"..wstring.format(L"%d", critBonusTotal)..L"%"
        return retString
--]]
        return
    elseif (5 == index) then
        return
    elseif (6 == index) then
        return
    elseif (7 == index) then
        return
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetMagicLabelsTooltipLine2(index)
    if (1 == index) then
        return CharacterWindow.GetDamageBonusTooltipDesc(GameData.BonusTypes.EBONUS_DAMAGE_MAGIC, GetString( StringTables.Default.LABEL_MAGIC ))
    elseif (2 == index) then
        return CharacterWindow.GetCritBonusTooltipDesc(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MAGIC, GetString( StringTables.Default.LABEL_ATTACK ) )
    elseif (3 == index) then
        return CharacterWindow.GetCritBonusTooltipDesc(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_HEALING, GetString( StringTables.Default.LABEL_HEAL_LOWER ) )
    elseif (4 == index) then
        return --CharacterWindow.GetCritDamageTooltipDesc( L"spell" )
    elseif (5 == index) then
        return
    elseif (6 == index) then
        return
    elseif (7 == index) then
        return
    elseif (8 == index) then
        return
    end
    
    return
end

function CharacterWindow.GetBasicStatTooltip(statID, currentValue)
        local retString = L""..L": "
        retString = retString..currentValue
        retString = retString..L"("
        retString = retString..GameData.Player.Stats[statID].baseValue
        local bonusValue = currentValue-GameData.Player.Stats[statID].baseValue
        if( bonusValue > 0 )
        then
            retString = retString..L"+"
        end
        if( bonusValue ~= 0 )
        then
            retString = retString..bonusValue
        end
        retString = retString..L")"
        return retString
end

function CharacterWindow.GetBasicStatPercentageTooltip(statID, currentValue )
        if (currentValue > 100 )
        then
            currentValue = 100
        end
        local retString = L""..L": "
        retString = retString..wstring.format( L"%.01f", currentValue )
        retString = retString..L"("
        retString = retString..wstring.format( L"%.01f", GameData.Player.Stats[statID].baseValue / 100 )
        retString = retString..L"+"
        retString = retString..wstring.format( L"%.01f", ( currentValue - ( GameData.Player.Stats[statID].baseValue / 100 ) ) )
        retString = retString..L")"
        return retString
end

function CharacterWindow.GetOffensiveStatTooltip(statID)
        local retString = L""..L": "
        retString = retString..CharacterWindow.CalcOffensiveStat(statID)
        return retString
end

function CharacterWindow.CalcOffensiveStat(statID)
    local rightData = 0
    if statID == 1 then
        if (CharacterWindow.equipmentData[1].dps > 0) then
            return wstring.format(L"%.01f",CharacterWindow.equipmentData[1].dps)
        else
            return L"0"
        end
    elseif statID == 2 then
        if (CharacterWindow.equipmentData[2].dps > 0) then
            return wstring.format(L"%.01f",CharacterWindow.equipmentData[2].dps)
        else
            return L"0"
        end
    elseif statID == 3 then
        rightData = (GameData.Player.Stats[7].currentValue/(GameData.Player.battleLevel*5+50)*.25)*100.0
        return wstring.format(L"%.01f",rightData)
    elseif statID == 4 then
        if (CharacterWindow.equipmentData[3].dps > 0) then
            return wstring.format(L"%.01f",CharacterWindow.equipmentData[3].dps)
        else
            return L"0"
        end
    elseif statID == 5 then
        rightData = GameData.Player.Stats[9].currentValue/5
        return wstring.format(L"%d",rightData)
    elseif statID == 6 then
        rightData = GameData.Player.Stats[3].currentValue/5
        return wstring.format(L"%d",rightData)
    elseif statID == 7 then
        return L"N/A"
    elseif statID == 8 then
        return L"N/A"
    end
end

function CharacterWindow.PaperDollMouseUp()
    if Cursor.IconOnCursor() and CharacterWindow.dropPending == false then
        CharacterWindow.AutoEquipItem(Cursor.Data.SourceSlot)
    end
end

function CharacterWindow.UpdateStatIcon(wndName, iconNum)
    if (wndName == nil) or (wndName == "") then
        return
    end
    if (iconNum == 0) then
        WindowSetShowing(wndName, false)
        return
    end
    
    local texture, x, y = GetIconData( iconNum ) 
    DynamicImageSetTexture( wndName, texture, x, y )
    WindowSetShowing(wndName, true)
end

function CharacterWindow.UpdateStatIcons()
    local texture, x, y
    -- Stats selections
    if CharacterWindow.currentStatSelection == 1 then
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow1IconBase", CharacterWindow.StatIconInfo[GameData.Stats.STRENGTH].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow2IconBase", CharacterWindow.StatIconInfo[GameData.Stats.BALLISTICSKILL].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow3IconBase", CharacterWindow.StatIconInfo[GameData.Stats.INTELLIGENCE].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", CharacterWindow.StatIconInfo[GameData.Stats.TOUGHNESS].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", CharacterWindow.StatIconInfo[GameData.Stats.WEAPONSKILL].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow6IconBase", CharacterWindow.StatIconInfo[GameData.Stats.INITIATIVE].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", CharacterWindow.StatIconInfo[GameData.Stats.WILLPOWER].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow8IconBase", CharacterWindow.StatIconInfo[GameData.Stats.WOUNDS].iconNum )
    -- Defense selections
    elseif CharacterWindow.currentStatSelection == 2 then
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow1IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_ARMOR].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow2IconBase", CharacterWindow.StatIconInfo[GameData.Stats.SPIRITRESIST].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow3IconBase", CharacterWindow.StatIconInfo[GameData.Stats.CORPOREALRESIST].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", CharacterWindow.StatIconInfo[GameData.Stats.ELEMENTALRESIST].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", CharacterWindow.StatIconInfo[GameData.Stats.BLOCKSKILL].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow6IconBase", CharacterWindow.StatIconInfo[GameData.Stats.PARRYSKILL].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", CharacterWindow.StatIconInfo[GameData.Stats.EVADESKILL].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow8IconBase", CharacterWindow.StatIconInfo[GameData.Stats.DISRUPTSKILL].iconNum )
    elseif CharacterWindow.currentStatSelection == 3 then
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow1IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_WEAPON_DPS].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow2IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_BONUS].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow3IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_ARMOR_PENETRATION].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_SPEED].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow6IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_CRIT_BONUS].iconNum )
        --CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_MELEE_CRIT_DAMAGE].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow8IconBase", 0 )
    elseif CharacterWindow.currentStatSelection == 4 then
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow1IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_RANGED].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow2IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_SPEED].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow3IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_BONUS].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_CRIT_BONUS].iconNum )
        --CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_RANGED_CRIT_DAMAGE].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow6IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow8IconBase", 0 )
    elseif CharacterWindow.currentStatSelection == 5 then
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow1IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_SPELL_BONUS].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow2IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_DAMAGE_CRIT_PERCENT].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow3IconBase", CharacterWindow.StatIconInfo[StringTables.Default.LABEL_HEAL_CRIT_PERCENT].iconNum )
        --CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", CharacterWindow.StatIconInfo[CharacterWindow.LABEL_SPELL_CRIT_DAMAGE].iconNum )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow4IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow5IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow6IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow7IconBase", 0 )
        CharacterWindow.UpdateStatIcon("CharacterWindowContentsStatsWindow8IconBase", 0 )
    end
end

function CharacterWindow.CreateTooltip(wndName, line1, line2)
    if (line1 == nil) then
        return
    end
    Tooltips.CreateTextOnlyTooltip( wndName )
    Tooltips.SetTooltipText( 1, 1, line1 )
    Tooltips.SetTooltipColorDef( 1, 1, Tooltips.COLOR_HEADING )
    if (line2 ~= nil) then
        Tooltips.SetTooltipText( 2, 1, line2 )  
    end
    Tooltips.Finalize()
    Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT )
end

function CharacterWindow.OnVisibleEquipmentUpdated( showHelm, showCloak, showCloakHeraldry )
    ButtonSetPressedFlag( "CharacterWindowContentsEquipmentShowHelm", showHelm )
    ButtonSetPressedFlag( "CharacterWindowContentsEquipmentShowCloak", showCloak )
    ButtonSetPressedFlag( "CharacterWindowContentsEquipmentShowCloakHeraldry", showCloakHeraldry )
    
    if( showCloak )
    then
        ButtonSetDisabledFlag("CharacterWindowContentsEquipmentShowCloakHeraldry", false)
    else
        ButtonSetDisabledFlag("CharacterWindowContentsEquipmentShowCloakHeraldry", true)
    end
end

function CharacterWindow.UpdateResistLabel( wndName, leftText, rightText, currentVal, baseVal )
    LabelSetText( wndName.."Left", leftText )
    LabelSetText( wndName.."Right", rightText )
    if( CharacterWindow.IsResistDiminished( currentVal ) == true )
    then
        LabelSetTextColor( wndName.."Right", DefaultColor.ChatTextColors[23].r, DefaultColor.ChatTextColors[23].g, DefaultColor.ChatTextColors[23].b )
    else
        CharacterWindow.UpdateLabel( wndName, leftText, rightText, currentVal, baseVal )
    end
end

function CharacterWindow.UpdateStatLabel( wndName, leftText, rightText, currentVal, baseVal )
    LabelSetText( wndName.."Left", leftText )
    LabelSetText( wndName.."Right", rightText )
    if( CharacterWindow.IsStatDiminished( currentVal ) == true )
    then
        LabelSetTextColor( wndName.."Right", DefaultColor.ChatTextColors[23].r, DefaultColor.ChatTextColors[23].g, DefaultColor.ChatTextColors[23].b )
    else
        CharacterWindow.UpdateLabel( wndName, leftText, rightText, currentVal, baseVal )
    end
end

function CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
    LabelSetText( wndName.."Left", leftText )
    LabelSetText( wndName.."Right", rightText )
    if currentVal > baseVal then
        LabelSetTextColor( wndName.."Right", DefaultColor.ChatTextColors[59].r, DefaultColor.ChatTextColors[59].g, DefaultColor.ChatTextColors[59].b )
    elseif currentVal < baseVal then
        LabelSetTextColor( wndName.."Right", DefaultColor.RED.r, DefaultColor.RED.g, DefaultColor.RED.b )
    else
        LabelSetTextColor( wndName.."Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    end
end


function CharacterWindow.UpdateStrengthLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_STRENGTH ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.STRENGTH].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_STRENGTH, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )
    local rightText = L""..currentVal
    g_currentStrength = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateToughnessLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_TOUGHNESS ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.TOUGHNESS].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_TOUGHNESS, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal ) 
    local rightText = L""..currentVal
    g_currentToughness = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateWoundsLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_WOUNDS ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.WOUNDS].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_WOUNDS, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )   
    local rightText = L""..currentVal
    g_currentWounds = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateInitiativeLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_INITIATIVE ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.INITIATIVE].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_INITIATIVE, baseVal ) 
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )     
    local rightText = L""..currentVal
    g_currentInitiative = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateWeaponskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_WEAPONSKILL ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.WEAPONSKILL].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_WEAPONSKILL, baseVal ) 
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )  
    local rightText = L""..currentVal
    g_currentWeaponskill = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateBallisticskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BALLISTICSKILL ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.BALLISTICSKILL].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_BALLISTICSKILL, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )
    local rightText = L""..currentVal
    g_currentBallisticskill = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateIntelligenceLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_INTELLIGENCE ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.INTELLIGENCE].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_INTELLIGENCE, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )
    local rightText = L""..currentVal    
    g_currentIntelligence = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateWillpowerLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_WILLPOWER ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.WILLPOWER].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_WILLPOWER, baseVal )
    baseVal, currentVal = GetEffectiveStatValue( baseVal, currentVal )
    local rightText = L""..currentVal    
    g_currentWillpower = currentVal
    CharacterWindow.UpdateStatLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateArmorLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_ARMOR ))..L":"
    local rightText = L""..GameData.Player.armorValue
    local currentVal = GameData.Player.armorValue
    local baseVal = GameData.Player.armorValue
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateCorporealResistLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_CORPOREALRESIST ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.CORPOREALRESIST].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CORPOREAL_RESIST, baseVal )
    local rightText = L""..currentVal
    g_currentCorporeal = currentVal
    CharacterWindow.UpdateResistLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateSpiritResistLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_SPIRITRESIST ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.SPIRITRESIST].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_SPIRIT_RESIST, baseVal )
    local rightText = L""..currentVal
    g_currentSpirit = currentVal
    CharacterWindow.UpdateResistLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateElementalResistLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_ELEMENTALRESIST ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.ELEMENTALRESIST].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_ELEMENTAL_RESIST, baseVal )
    local rightText = L""..currentVal
    g_currentElemental = currentVal    
    CharacterWindow.UpdateResistLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateBlockskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS_BLOCK ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.BLOCKSKILL].baseValue / 100
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_BLOCK, baseVal ) 
    if( currentVal > 100 )
    then
        currentVal = 100
    end
    local rightText = wstring.format(L"%.01f",currentVal)
    g_currentBlock = currentVal
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateParryskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS_PARRY ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.PARRYSKILL].baseValue / 100
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_PARRY, baseVal )
    if( currentVal > 100 )
    then
        currentVal = 100
    end
    local rightText = wstring.format(L"%.01f",currentVal)
    g_currentParry = currentVal
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateEvadeskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS_EVADE ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.EVADESKILL].baseValue / 100
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_EVADE, baseVal )
    if( currentVal > 100 )
    then
        currentVal = 100
    end
    local rightText = wstring.format(L"%.01f",currentVal)
    g_currentEvade = currentVal
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateDisruptskillLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS_DISRUPT ))..L":"
    local baseVal = GameData.Player.Stats[GameData.Stats.DISRUPTSKILL].baseValue / 100
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_DISRUPT, baseVal )
    if( currentVal > 100 )
    then
        currentVal = 100
    end
    local rightText = wstring.format(L"%.01f",currentVal)
    g_currentDisrupt = currentVal
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.GetMeleeDPSLabelLeft(bTruncate)
    local retText = GetString( StringTables.Default.LABEL_WEAPON_DPS )
    if bTruncate then
        retText = CharacterWindow.AdjustStatLabel(retText)
    end
    
    retText = retText..L":"
    return retText
end

function CharacterWindow.GetMeleeDPSLabelRight()
    local retText = L""
    if (CharacterWindow.equipmentData[GameData.ItemSlots.RIGHT_HAND].dps > 0) then
        retText = wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RIGHT_HAND].dps)
    else
        retText = L"0"
    end
    if (CharacterWindow.equipmentData[GameData.ItemSlots.LEFT_HAND].dps > 0) then
        retText = retText..L" / "..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.LEFT_HAND].dps)
    else
        retText = retText..L" / "..L"0"
    end
    return retText
end

function CharacterWindow.UpdateMeleeDPSLabel(wndName)
    local leftText = CharacterWindow.GetMeleeDPSLabelLeft(true)
    local rightText = CharacterWindow.GetMeleeDPSLabelRight()
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateMeleeBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS ))..L":"
    local rightText = L""..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_MELEE, 0)
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.CalcArmorPenetration()
    return wstring.format(L"%.01f",((g_currentWeaponskill/(GameData.Player.battleLevel*7.5+50)*.25)*100.0))
end

function CharacterWindow.UpdateArmorPenetrationLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_MELEE_ARMOR_PENETRATION ))..L":"
    local baseVal = GameData.Player.Stats[GameData.StatTypes.WEAPON_SKILL].baseValue
    local currentVal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_WEAPONSKILL, baseVal )
    g_currentWeaponskill = currentVal
    local rightText = L""..CharacterWindow.CalcArmorPenetration()..L"%"
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateMeleeSpeedLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_SPEED ))..L":"
    local rightText = L""..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RIGHT_HAND].speed)
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateMeleeCritBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_CRIT_BONUS ))..L":"
    local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MELEE, 0)
    critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
    local rightText = L""..critBonus..L"%"
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateRangedDPSLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_WEAPON_DPS ))..L":"
    local rightText = L""
    if (CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].dps > 0) then
        rightText = rightText..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].dps)
    else
        rightText = rightText..L"0"
    end
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateRangedSpeedLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_SPEED ))..L":"
    local rightText = L""..wstring.format(L"%.01f",CharacterWindow.equipmentData[GameData.ItemSlots.RANGED].speed)
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateRangedBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS ))..L":"
    local rightText = L""..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_RANGED, 0)
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateRangedCritBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_CRIT_BONUS ))..L":"
    local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_RANGED, 0)
    critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
    local rightText = L""..critBonus..L"%"
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateSpellBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_BONUS ))..L":"
    local rightText = L""..CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_DAMAGE_MAGIC, 0)
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateSpellCritBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_CRIT_BONUS ))..L":"
    local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_MAGIC, 0)
    critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
    local rightText = L""..critBonus..L"%"
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateSpellHealCritBonusLabel(wndName)
    local leftText = CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_CRIT_BONUS ))..L":"
    local critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE_HEALING, 0)
    critBonus = CharacterWindow.CalculateValueWithBonus(GameData.BonusTypes.EBONUS_CRITICAL_HIT_RATE, critBonus)
    local rightText = L""..critBonus..L"%"
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.GetBaseCritDamage( attackType )
    -- Adding 0.5 since we always floor the value we display
    if( attackType == L"melee" )
    then
        return 0.5 + ( 50 * g_currentWeaponskill / ( GameData.Player.battleLevel * 7.5 + 50 ) )
    elseif( attackType == L"ranged" )
    then
        return 0.5 + ( 50 * g_currentWeaponskill / ( GameData.Player.battleLevel * 7.5 + 50 ) )
    else    -- spell
        return 0.5 + ( 50 * g_currentWillpower / ( GameData.Player.battleLevel * 7.5 + 50 ) )
    end
end

function CharacterWindow.UpdateMeleeCritDamageLabel( wndName )
    CharacterWindow.UpdateCritDamageLabel( wndName, L"melee" )
end

function CharacterWindow.UpdateRangedCritDamageLabel( wndName )
    CharacterWindow.UpdateCritDamageLabel( wndName, L"ranged" )
end

function CharacterWindow.UpdateSpellCritDamageLabel( wndName )
    CharacterWindow.UpdateCritDamageLabel( wndName, L"spell" )
end

function CharacterWindow.UpdateCritDamageLabel( wndName, attackType )
    local leftText = CharacterWindow.AdjustStatLabel( GetString( StringTables.Default.LABEL_CRIT_DMG ) )..L":"
    local critBaseDamage = CharacterWindow.GetBaseCritDamage( attackType )
    local critBonusTotal = CharacterWindow.CalculateValueWithBonus( GameData.BonusTypes.EBONUS_CRITICAL_DAMAGE, critBaseDamage )
    critBonusTotal = Clamp( critBonusTotal, 5.0, 75.0 )
    local rightText = L""..wstring.format(L"%d", critBonusTotal)..L"%"
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.UpdateBaseLabel(wndName)
    local leftText = ""
    local rightText = L""
    local currentVal = 0
    local baseVal = 0
    CharacterWindow.UpdateLabel(wndName, leftText, rightText, currentVal, baseVal)
end

function CharacterWindow.CalculateValueWithBonus(bonusType, baseValue)
    return GetBonus(bonusType, baseValue)
end

function CharacterWindow.RefreshStatsData()
	CharacterWindow.UpdateStatsNew()
	WindowUnregisterEventHandler ("CharacterWindow", SystemData.Events.ITEM_SET_DATA_UPDATED )
	CharacterWindow.registeredForItemSetData = nil
end



function CharacterWindow.UpdateOffenseLabels()
    local rightText=L"0"
    local rightData = 0

    rightText = CharacterWindow.CalcOffensiveStat(1)
    LabelSetTextColor( "CharacterWindowStatsWindow1Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow1Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_MAIN_HAND ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow1Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(2)
    LabelSetTextColor( "CharacterWindowStatsWindow2Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow2Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_OFF_HAND ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow2Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(3)
    LabelSetTextColor( "CharacterWindowStatsWindow3Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow3Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_ARMOR_PENETRATION ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow3Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(4)
    LabelSetTextColor( "CharacterWindowStatsWindow4Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow4Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_RANGED ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow4Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(5)
    LabelSetTextColor( "CharacterWindowStatsWindow5Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow5Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_SPELL_BONUS ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow5Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(6)
    LabelSetTextColor( "CharacterWindowStatsWindow6Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow6Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_HEALING_BONUS ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow6Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(7)
    LabelSetTextColor( "CharacterWindowStatsWindow7Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow7Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_DAMAGE_CRIT_PERCENT ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow7Right", rightText )

    rightText = CharacterWindow.CalcOffensiveStat(8)
    LabelSetTextColor( "CharacterWindowStatsWindow8Right", DefaultColor.WHITE.r, DefaultColor.WHITE.g, DefaultColor.WHITE.b )
    LabelSetText( "CharacterWindowStatsWindow8Left", CharacterWindow.AdjustStatLabel(GetString( StringTables.Default.LABEL_HEAL_CRIT_PERCENT ))..L":" )
    LabelSetText( "CharacterWindowStatsWindow8Right", rightText )
end

function CharacterWindow.PlayerEffectsUpdated()
    CharacterWindow.UpdateStatsNew()
end

function CharacterWindow.MarketingLButtonDown()
    -- 
    local slot = WindowGetId(SystemData.ActiveWindow.name)  

    if( Cursor.IconOnCursor() ) then
        --    
        if( CharacterWindow.marketItemData == nil ) then
            local itemData = DataUtils.GetItems()[Cursor.Data.SourceSlot]            
            if( Cursor.Data.Source == Cursor.SOURCE_INVENTORY and itemData.type == GameData.ItemTypes.MARKETING) then                
                local slot = Cursor.Data.SourceSlot
                CharacterWindow.marketItemData = itemData
                CharacterWindow.marketItemDataSlot = slot
                local texture, x, y = GetIconData(itemData.iconNum)
                DynamicImageSetTexture( "DyeMerchantMarketingSlotIconBase", texture, x, y )
                Cursor.Clear()
                CharacterWindow.MarketingMouseOver()
                SetPlayerVariation(slot)
                LabelSetText("DyeMerchantMarketingSlotDescLabel", GetString( StringTables.Default.TEXT_MARKETING_ITEM_SLOT_REMOVE ) )           
            else
                -- Place error popup here
                DialogManager.MakeOneButtonDialog( GetString( StringTables.Default.TEXT_MARKETING_ITEM_ERROR ), GetString( StringTables.Default.LABEL_OKAY ) )
                Cursor.Clear()
                CharacterWindow.MarketingMouseOver()                
            end
        else
            CharacterWindow.marketItemData = nil
            CharacterWindow.marketItemDataSlot = -1
            RevertPlayerVariation();
            CharacterWindow.MarketingLButtonDown()
        end            
    else
        -- 
    end
end

function CharacterWindow.MarketingRButtonDown()
    -- 
    if( CharacterWindow.marketItemData == nil ) then
    -- do nothing
    else
        CharacterWindow.marketItemData = nil
        CharacterWindow.marketItemDataSlot = -1 
        local icon = CharacterWindow.TROPHY_EMPTY_ICON
        DynamicImageSetTexture( "DyeMerchantMarketingSlotIconBase", icon.texture, icon.x, icon.y )
        LabelSetText("DyeMerchantMarketingSlotDescLabel", GetString( StringTables.Default.TEXT_MARKETING_ITEM_SLOT_TIP ) )           
        CharacterWindow.MarketingMouseOver()
        RevertPlayerVariation();
    end
end

function CharacterWindow.MarketingMouseOver()
    --    
    local itemData = CharacterWindow.marketItemData    
  
    if( itemData == nil ) then
        Tooltips.CreateTextOnlyTooltip( "DyeMerchantMarketingSlot", nil )
        Tooltips.SetTooltipText( 1, 1, GetString( StringTables.Default.LABEL_MARKETING_ITEM_SLOT ) )
        Tooltips.SetTooltipColor( 1, 1, 123, 172, 220 )
        Tooltips.Finalize()
        Tooltips.AnchorTooltip( Tooltips.ANCHOR_WINDOW_RIGHT )
    else
        Tooltips.CreateItemTooltip( itemData, "DyeMerchantMarketingSlot", Tooltips.ANCHOR_WINDOW_RIGHT, true )   
    end
         
end

function CharacterWindow.ApplyRewardItem()
    -- 
    if( CharacterWindow.marketItemDataSlot ~= -1 ) then
        ApplyPlayerVariation(CharacterWindow.marketItemDataSlot)
        CharacterWindow.marketItemData = nil
        CharacterWindow.marketItemDataSlot = -1 
        local icon = CharacterWindow.TROPHY_EMPTY_ICON
        DynamicImageSetTexture( "DyeMerchantMarketingSlotIconBase", icon.texture, icon.x, icon.y )
        if( Cursor.IconOnCursor() ) then
            Cursor.Clear()
        end
        CharacterWindow.HideDyeMerchant()
        LabelSetText("DyeMerchantMarketingSlotDescLabel", GetString( StringTables.Default.TEXT_MARKETING_ITEM_SLOT_TIP ) )           
    else
        -- we shouldn't really be here
    end
end

function CharacterWindow.ResetListBackground( )
    local color = DefaultColor.BLACK    -- make the background black
    color.a = 1                         -- and opaque
    for row = 1, CharacterWindowTimeoutsList.numVisibleRows do
        local targetRowWindow = "CharacterWindowTimeoutsListRow"..row
        WindowSetTintColor(targetRowWindow.."Background", color.r, color.g, color.b )
        WindowSetAlpha(targetRowWindow.."Background", color.a )
    end
end

-- the slash command has been changed to provide a return message for use in the timeout/lockout tab
function CharacterWindow.RequestTimeoutInfo()
    SystemData.UserInput.ChatText = L"/lockout"
    BroadcastEvent( SystemData.Events.SEND_CHAT_TEXT )
end

-- this is the function called when the lockouts updated event is recieved
function CharacterWindow.LockoutsUpdated( lockouts )
    -- keep track of the lockout table
    CharacterWindow.timeoutsList = lockouts
    -- this boolean is used to make sure we are synced up at least once
    -- when the player opens the timeout tab for the first time
    CharacterWindow.firstTimeoutsync = false
    -- update the listbox with the table data
    CharacterWindow.UpdateLockouts()
end

function CharacterWindow.UpdateLockouts()
    -- reset the background to all black
    CharacterWindow.ResetListBackground()
    -- reset the timeout list
    CharacterWindow.timeoutList = {}
    
    local displayOrder = {}

    local hasTimeouts = false
    local rowCount = 1 -- this is used to keep the rows that are grouped together the same color
    for index, timeout in ipairs( CharacterWindow.timeoutsList )
    do
        -- if we have one or more time outs we'll set it here so that the no timeout text isn't added
        hasTimeouts = true
        -- alternate the row colors
        local row_mod = math.mod(index, 2)
        color = DataUtils.GetAlternatingRowColor( row_mod )

        CharacterWindow.timeoutList[rowCount] = {}
        CharacterWindow.timeoutList[rowCount].zoneStr = GetZoneName(timeout.zoneID)
        CharacterWindow.timeoutList[rowCount].timeStr = CharacterWindow.TimeString( timeout.timeRemaining )
        CharacterWindow.timeoutList[rowCount].whoStr = L""
        CharacterWindow.timeoutList[rowCount].color = color

        -- add the row # to the display order list
        table.insert(displayOrder, rowCount)
        -- go to the next row for either a kill row or the next lockout group
        rowCount = rowCount + 1

        for indexKill, kill in ipairs( timeout.killList )
        do
            CharacterWindow.timeoutList[rowCount] = {}
            CharacterWindow.timeoutList[rowCount].zoneStr = L""
            CharacterWindow.timeoutList[rowCount].timeStr = L""
            CharacterWindow.timeoutList[rowCount].whoStr = CharacterWindow.KillString(kill)
            CharacterWindow.timeoutList[rowCount].color = color

            -- add the row # to the display order list
            table.insert(displayOrder, rowCount)
            --go to the next row for either a kill row or the next lockout group
            rowCount = rowCount + 1
        end
    end

    if not hasTimeouts
    then
        CharacterWindow.timeoutList[1] = {}
        CharacterWindow.timeoutList[1].zoneStr = L""
        CharacterWindow.timeoutList[1].timeStr = L""
        CharacterWindow.timeoutList[1].whoStr = GetString( StringTables.Default.LABEL_TIMEOUTS_NO_TIMEOUTS )
        CharacterWindow.timeoutList[1].color = DataUtils.GetAlternatingRowColor( 1 )

        -- add the row # to the display order list
        table.insert(displayOrder, 1)
    end
    
    -- there is currently no sorting but the listbox needs to know how many lines to display
    ListBoxSetDisplayOrder("CharacterWindowTimeoutsList", displayOrder )

end

-- calculate the number of days, hours, and minutes from the minutes remaining field of the lockout
function CharacterWindow.TimeString( minutes )
    local days = 0
    local hours = 0
    local remainingHours = 0
    local remainingMinutes = math.fmod(minutes, 60)
    minutes = minutes - remainingMinutes
    
    if minutes > 0
    then
        hours = minutes / 60
        remainingHours = math.fmod(hours, 24)
        hours = hours - remainingHours
        if hours > 0
        then
            days = hours / 24
        end
    end

    return GetStringFormat( StringTables.Default.LABEL_TIMEOUTS_TIME_FORMAT, { days, remainingHours, remainingMinutes} )
end

-- I went ahead and made this a function because I suspect that in the future we will have cases where the "kill string"
-- isn't necessarily from a kill
function CharacterWindow.KillString( name )
    return GetStringFormat( StringTables.Default.LABEL_TIMEOUT_KILL, {name} )
end

-- this is called in the update loop and it keeps track of the elapsed time in order to
-- update the lockout times on the client without having to spam the server for an update
-- so that the client stays in sync with the server we will resync every 10 minutes
function CharacterWindow.UpdateTimeouts( elapsedTime )
    -- add the ela[sed time to our count
    CharacterWindow.timeoutElapsedTime = CharacterWindow.timeoutElapsedTime + elapsedTime
    -- has it been a minute?
    if CharacterWindow.timeoutElapsedTime > 60
    then
        -- if so reset our count
        CharacterWindow.timeoutElapsedTime = CharacterWindow.timeoutElapsedTime - 60
        -- update the timeout table's times
        CharacterWindow.DecrementLockouts()
        -- update the listbox with the new times
        CharacterWindow.UpdateLockouts()
    end
end

-- this is where we update the timeout times on the client
function CharacterWindow.DecrementLockouts()
    for index, timeout in pairs( CharacterWindow.timeoutsList )
    do
        timeout.timeRemaining = timeout.timeRemaining - 1
        -- if the new time remaining is zero or less we should sync up with the server 
        -- so this one can be removed from the list
        if timeout.timeRemaining <= 0
        then
            CharacterWindow.RequestTimeoutInfo()
            return -- since we are syncing we might as well not bother to finish our timer updates
        end
    end
end

-- called when the timeout tab is shown
function CharacterWindow.OnTimeoutShown()
    -- if for some reason we haven't gotten a timeout sync from the server we will request one
    if CharacterWindow.firstTimeoutsync
    then
        CharacterWindow.RequestTimeoutInfo()
        -- set to false so that this doesn't get spammed if the server isn't responding for some reason
        CharacterWindow.firstTimeoutsync = false
    end
end

-- used when the list changes what lines are shown, when the list box is scrolled for example
function CharacterWindow.PopulateTimeoutList()
    if (CharacterWindowTimeoutsList.PopulatorIndices ~= nil) then				
        for rowIndex, dataIndex in ipairs (CharacterWindowTimeoutsList.PopulatorIndices) do
            local data = CharacterWindow.timeoutList[ dataIndex ]
            -- this update is used to maintain the proper coloring of the lines
            CharacterWindow.UpdateModRowByIndex( rowIndex, data )				
        end
    end    
end

-- color the lines based on the color set when the lines were initially updated
function CharacterWindow.UpdateModRowByIndex( rowIndex, data )
    local targetRowWindow = "CharacterWindowTimeoutsListRow"..rowIndex
    WindowSetTintColor(targetRowWindow.."Background", data.color.r, data.color.g, data.color.b )
    WindowSetAlpha(targetRowWindow.."Background", data.color.a )
end
